Convert an ASCII String to Fixed Point: atofp()

conversion

Here is a small utility routine which converts an ASCII string floating point number into an s16.15 format fixed point number. Most fixed point libraries neglect this conversion. However, in practice, this routine is very useful. If the conversion process is not efficient, the gains of using fixed point over floating point math can be eliminated. Having said that, this is not pretty code and neither is it efficient. And it breaks a few coding rules too.

It is also important to note, the routine does very little (almost no) validity testing of the input values (size of integer/fixed point numbers, valid characters, sufficient string space, etc.). So there is plenty of opportunity here for spectacular failure.

The complementary conversion, fptoa() is also included. The required header file is here.

#define MAX_STRING_SIZE 8

//atol function ignores sign
int32_t _atol(const char* s) {
  int32_t v=0;
   
  //ignore whitespace
  while (*s == ' ' || (uint16_t)(*s - 9) < 5u) 
    ++s;
  //skip over sign
  if (*s == '-' || *s == '+') 
    ++s;
  //convert ascii to number
  while ((uint16_t)(*s - '0') < 10u) {
    v = v*10 + *s - '0';
    ++s;
  }
  return v;
}

//basic string copy n
static inline void _strcpy(char *d, const char *s) {
  uint8_t n=0;
   
  while (*s != '\0') {
    if (n++ >= MAX_STRING_SIZE) 
      return; //destination @ max size
    *d++ = *s++;
  }
}

//basic string concatenation
void _concat(char *d, char *s) {
  uint8_t n=0;
  
  //find end of destination
  while(*d) {
    d++;
  }
  //copy source to destination
  while(*s && n<MAX_STRING_SIZE) {
    *d++ = *s++;
    n++;
  }
  //terminate destination
  *d = '\0';
}

//int32_t atofp(char *) 
int32_t StringToFixed(char *s) {
  int32_t f, fpw, fpf, bit, r[15] = {
    0x2faf080, 0x17d7840, 0xbebc20, 0x5f5e10, 0x02faf08, 0x017d784, 0x0bebc2, 0x05f5e1,
    0x002faf1, 0x0017d78, 0x00bebc, 0x005f5e, 0x0002faf, 0x00017d8, 0x000bec //0x0005f6
  };
  uint8_t sign, i;
  char *p=s, temp[9] = "00000000";

  sign = 0;
  //separate whole & fraction portions
  while (*p != '.') {
    //check for negative sign
    if (*p == '-') 
      sign = 1;
    //no decimal found, return integer as fixed point
    if (*p == '\0') 
      return sign ? -(_atol(s)<<FP_FBITS) : (_atol(s)<<FP_FBITS);
    p++;
  }
 
  //whole part
  *p = '\0';
  fpw = (_atol(s)<<FP_FBITS);
 
  //pad fraction part with trailing zeros
  _strcpy(temp, (p + 1));
  //get fraction
  f = _atol(temp);
  //re-insert decimal point
  *p = '.';
 
  fpf = 0;
  bit = 0x4000;
  //convert base10 fraction to fixed point base2
  for (i=0; i<15; i++) {
    if (f - r[i] > 0) {
      f -= r[i];
      fpf += bit;
    }
    bit >>= 1;
  }
 
  //join fixed point whole and fractional parts
  return sign ? -(fpw + fpf) : (fpw + fpf);
}

//void fptoa(int32_t, char *)
void FixedToString(int32_t f, char *s) {
  int32_t fp, bit=0x4000, r[16] = { 50000, 25000, 12500, 6250, 3125, 1563, 781, 391, 195, 98, 49, 24, 12, 6, 3 };
  int32_t d[5] = { 10000, 1000, 100, 10 };
  char *p=s, *sf, temp[12];
  uint8_t i;
  
  //zero?
  if (f == 0) {
    *p++ = '0';
    *p = 0;
    return;
  }
  
  //negate?
  if (f < 0) {
    *p++ = '-';
    f = -f;
  }
  
  //get whole part
  fp = ktoi(f);
  if (fp != 0) 
    p = _ltoa(fp, p);
  else 
    *p++ = '0';
    
  //get fractional part
  fp = FP_FRAC_PART(f);
  if (fp == 0) {
    *p = 0;   //terminate string
    return;
  }  
  *p++ = '.'; //add decimal to end of s
  *p = 0;     //terminate string
   
  f = 0;
  //convert fraction base 2 to base 10
  for (i=0; i<15; i++) {
    if (fp & bit) 
      f += r[i];
    bit >>= 1;
  }
  //temporary string storage space
  sf = temp;
  sf = ltoa(f, sf, 10);
   
  // if needed, add leading zeros to fractional portion
  for (i=0; i<4; i++) {
    if (f < d[i]) {
      *p++ = '0';
      *p = '\0';
    } else 
      break;
  }
   
  //combine whole & fractional parts
  _concat(s, sf);
}
Advertisements

About Jim Eli

µC experimenter
This entry was posted in Uncategorized and tagged , , , , . Bookmark the permalink.

2 Responses to Convert an ASCII String to Fixed Point: atofp()

  1. Sean D'Epagnier says:

    I implemented the fixed point support into gcc some years ago, and I remember also implementing a complete math library (based on CORDIC) which was much smaller in code size (because all the routines share a core algorithm) compared to taylor series and also much faster on avrs without a mul instruction. I also implemented string conversion routines and support in printf/scanf in avrlibc.. but maybe those patches are lost?

    • Jim Eli says:

      Sean,
      AFAIK your fixed point patches are still alive in the avrlibc. I studied and intermixed some of your code with mine (I left attributions to your code in my post). Thanks for your excellent work. I am very interested in the CORDIC work you did. My only comment is that I can’t find any documentation anywhere. Thanks for the reply.
      Jim

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s