Lean and Mean Float to String Conversion (ftoa)

lean and mean

Here is an often requested, basic float to ascii conversion function (ftoa). The typical method for arduino to perform this conversion is to use either of the dtostrf() or sprintf() functions. Both of these library functions incorporate the same underlying avrlibc ftoa_engine assembly routine. Pulling all of the required background code into an arduino sketch brings considerable overhead.

To perform a few basic floating point-to-string conversions, the following ftoa function could save some space. My simple test shows that this ftoa saves approximately 816 bytes. Admittedly, it lacks the advanced formatting features found in dtostrf/sprintf, so if you need these, you’re stuck with the larger code.

I didn’t write this code, I just tweaked it slightly. And it’s not without flaws, so I highly suggest you test it and adapt for your purposes. Also, realize there are probably better alternatives. This simply worked for me. This source code is posted around the internet in various places, mostly without attribution. The earliest posting I found (2/2003 here) is a possible derivation, so credit to the anonymous author.

Code:

union int32_Float_t {
  int32_t Long;
  float Float;
};

#ifndef HUGE_VALF
#define HUGE_VALF (__builtin_huge_valf())
#endif

#ifndef FLT_MIN_EXP
#define FLT_MIN_EXP (-999)
#endif
#ifndef FLT_MAX_EXP
#define FLT_MAX_EXP (999)
#endif

#define _FTOA_TOO_LARGE -2  // |input| > 2147483520 
#define _FTOA_TOO_SMALL -1  // |input| < 0.0000001 

//precision 0-9
#define PRECISION 7

//_ftoa function 
void _ftoa(float f, char *p, int *status) {
  int32_t mantissa, int_part, frac_part;
  int16_t exp2;
  int32_Float_t x;

  *status = 0;
  if (f == 0.0) {
    *p++ = '0';
    *p++ = '.';
    *p++ = '0';
    *p = 0;
    return;
  }

  x.Float = f;
  exp2 = (unsigned char)(x.Long>>23) - 127;
  mantissa = (x.Long&0xFFFFFF) | 0x800000;
  frac_part = 0;
  int_part = 0;

  if (exp2 >= 31) {
    *status = _FTOA_TOO_LARGE;
    return;
  } else if (exp2 < -23) {
    *status = _FTOA_TOO_SMALL;
    return;
  } else if (exp2 >= 23) 
    int_part = mantissa<<(exp2 - 23);
  else if (exp2 >= 0) {
    int_part = mantissa>>(23 - exp2);
    frac_part = (mantissa<<(exp2 + 1))&0xFFFFFF;
  } else {
    //if (exp2 < 0)
    frac_part = (mantissa&0xFFFFFF)>>-(exp2 + 1);
  }

  if (x.Long < 0)
      *p++ = '-';
  if (int_part == 0)
    *p++ = '0';
  else {
    ltoa(int_part, p, 10);
    while (*p)
      p++;
  }
  *p++ = '.';
  if (frac_part == 0)
    *p++ = '0';
  else {
    char m;

    for (m=0; m<PRECISION; m++) {
      //frac_part *= 10;
      frac_part = (frac_part<<3) + (frac_part<<1); 
      *p++ = (frac_part>>24) + '0';
      frac_part &= 0xFFFFFF;
    }

    //delete ending zeroes
    for (--p; p[0] == '0' && p[-1] != '.'; --p)
      ;
    ++p;
  }
  *p = 0;
}

void setup(void) {
  int i, stat;
  char s[20];
  float f[] = { 0.0, -0.0, 42.0, 123.456789, 0.0000018, 555555.555, 
                   -888888888.8888888, 11111111.2 };
                   
  Serial.begin(9600);

  for ( i=0; i<8; i++ ) {
    if ( _ftoa(f[i], s, &stat) == 0 ) {
      Serial.print( "_ftoa: " ); 
      Serial.println( s );
    }
  }
}

void loop(void) { }
Advertisements

About Jim Eli

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

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