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); }

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?

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