float to string function

Hi.
If anyone has use for a float to string conversion function that runs standalone, I uploaded one to the playground here

http://www.arduino.cc/playground/Main/FloatToString

save that file to a floatToString.h in your project directory and go to town. (On my mac, I have to restart Arduino before I can import the file in a pde). There is example/test code in the comments field at the bottom of the file.

With this function, I have it require a char * buffer be passed in that it writes to.

If I wanted to create a new string inside that function, and return that instead, I assume that woud require malloc?
Anyways, I figured passing in a buffer from the location of your choice would be preferable anyway.

Hope it comes in handy, and please let me know if you find any bugs!

thanks,
Tim

Hi,

This is a useful function, but there appears to be a bug of not terminating the string properly when the output is "0".

Try running this code:

int K;
float M;
byte stepSize = 2;
char test[20];

void setup()
{
  Serial.begin(9600);
  Serial.println("Starting");
}

void loop()
{
  for (K = 10; K >= -10; K--){
    M = K/10.0;
    
    Serial.print(floatToString(test,M,0,7,true));
    Serial.print(' ');
    Serial.print(floatToString(test,M,1,7,true));
    Serial.print(' ');
    Serial.println(K, DEC);
    delay(100);
  }
}

This is the correct output:

Starting
     1     1.0 10
     1     0.9 9
     1     0.8 8
     1     0.7 7
     1     0.6 6
     1     0.5 5
     0     0.4 4
     0     0.3 3
     0     0.2 2
     0     0.1 1
     0     0.0 0
    -0    -0.1 -1
    -0    -0.2 -2
    -0    -0.3 -3
    -0    -0.4 -4
    -1    -0.5 -5
    -1    -0.6 -6
    -1    -0.7 -7

This is what you get instead

Starting
     1     1.0 10
     1     0.9 9
     1     0.8 8
     1     0.7 7
     1     0.6 6
     1     0.5 5
     05     0.4 4
     04     0.3 3
     03     0.2 2
     02     0.1 1
     01     0.0 0
    -00    -0.1 -1
    -01    -0.2 -2
    -02    -0.3 -3
    -03    -0.4 -4
    -1    -0.5 -5
    -1    -0.6 -6
    -1    -0.7 -7

Its worth noting that Don Kinzer published a routine for creating strings from floating point values here: http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1207226548/11#11

Don's code is smaller although it does not have the minwidth and rightjustify arguments.

I have seen this before and am curious, I notice that your code uses and instead of && . not instead of ! , I was wondering why this syntax is used?

I notice that your code uses and instead of && . not instead of ! , I was wondering why this syntax is used?

Heh I see you've highlighted the keywords, I had to read that sentence three times to correctly parse it ;D. I didn't even know you could use the keywords and and not in C!

mem I think your code is probably better, but I really need the right justify function for my OSD, since it makes changing numbers much easier to read. I think I'll try to combine both codes to get what I want...

Cheers,
-Z-

Mem,

Those "and" and "or" keywords come from iso646.h, which is automatically included, ostensibly to help people with "international" or "non-QWERTY" keyboards. To me it seems more problematic than useful. See http://en.wikipedia.org/wiki/Iso646.h.

Mikal

Hi,

I've looked around the forum, but couldn't find a FloatToStr function that actually worked with out any bugs.

  • Tim's code has some problem displaying zero.
  • Mem's code does not have proper rounding, does not allow setting text minimum width.
  • Don's code also has some issues displaying zero and negative values

So I've combined mem's code with Tim's code, and a little bit from Don. This hopefully should work as a compact sprintf() replacement that has no bugs (famous last words :D). Maybe it can be more efficient, but I think it's pretty good.

char * floatToString(char * outstr, double val, byte precision, byte widthp){
  char temp[16];
  byte i;

  // compute the rounding factor and fractional multiplier
  double roundingFactor = 0.5;
  unsigned long mult = 1;
  for (i = 0; i < precision; i++)
  {
    roundingFactor /= 10.0;
    mult *= 10;
  }
  
  temp[0]='\0';
  outstr[0]='\0';

  if(val < 0.0){
    strcpy(outstr,"-\0");
    val = -val;
  }

  val += roundingFactor;

  strcat(outstr, itoa(int(val),temp,10));  //prints the int part
  if( precision > 0) {
    strcat(outstr, ".\0"); // print the decimal point
    unsigned long frac;
    unsigned long mult = 1;
    byte padding = precision -1;
    while(precision--)
      mult *=10;

    if(val >= 0)
      frac = (val - int(val)) * mult;
    else
      frac = (int(val)- val ) * mult;
    unsigned long frac1 = frac;

    while(frac1 /= 10)
      padding--;

    while(padding--)
      strcat(outstr,"0\0");

    strcat(outstr,itoa(frac,temp,10));
  }

  // generate space padding 
  if ((widthp != 0)&&(widthp >= strlen(outstr))){
    byte J=0;
    J = widthp - strlen(outstr);
    
    for (i=0; i< J; i++) {
      temp[i] = ' ';
    }

    temp[i++] = '\0';
    strcat(temp,outstr);
    strcpy(outstr,temp);
  }
  
  return outstr;
}

Usage:
floatToString(buffer string, float value, precision, minimum text width)

set minimum text width to 0 for no right justify.

Example:

#include <string.h>

int K;
float M;
byte stepSize = 2;
char test[20];

void setup()
{
  Serial.begin(19200);
  Serial.println("Starting");
}

void loop()
{

  for (K = 1000; K >= -1000; K--){
    M = K/100.0;

    Serial.print(floatToString(test, M, 0, 3));
    Serial.print(' ');

    Serial.print(floatToString(test, M, 1, 5));
    Serial.print(' ');

    Serial.print(floatToString(test, M, 2, 6));
    Serial.print(' ');

    Serial.print(floatToString(test, M, 3, 7));
    Serial.print(' ');
    Serial.println(K, DEC);
    delay(10);
  }
}

Prints:

...
  0   0.1   0.07   0.070 7
  0   0.1   0.06   0.060 6
  0   0.1   0.05   0.050 5
  0   0.0   0.04   0.040 4
  0   0.0   0.03   0.030 3
  0   0.0   0.02   0.020 2
  0   0.0   0.01   0.010 1
  0   0.0   0.00   0.000 0
 -0  -0.0  -0.01  -0.010 -1
 -0  -0.0  -0.02  -0.020 -2
...

hello zitron,

I'm having rounding problems with your function. I did try to write my own ftoa function, but also have the same rounding issues as with your function. Check out this:

Serial.println(floatToString(str, 32766.6613, 2, 8));
Serial.println(floatToString(str, 32766.6613, 4, 8));
Serial.println(floatToString(str, 1.6613, 4, 8));
Serial.println(floatToString(str, -32766.1234, 2, 8));
Serial.println(floatToString(str, -32766.1234, 4, 8));

These are the results of your function:
32766.66 // CORRECT
32766.6621 // WRONG
1.6613 // CORRECT
-32766.12 // CORRECT
-32766.1230 // WRONG

Errors are small and probably won't make much difference, but still annoys me for not knowing the cause.

Any ideas?

Did you read the replies to your previous post yesterday?

http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1207226548/22

Hi gui,

Thanks for pointing out the mistake. I found out that there is a problem in the my function. Because the function itoa is used to convert numbers, and it is a integer (16bit) function, so there is a problem when you specify precision > 4 digits, the function therefore only works for values less than xxxx.xxxx.

I have modified it so that it uses ltoa instead, which uses long integers, so that should be plenty of digits. I'm also pretty much rewritten the function, so now it should be smaller and more efficient.

char * floatToString(char * outstr, double val, byte precision, byte widthp){
  char temp[16]; //increase this if you need more digits than 15
  byte i;

  temp[0]='\0';
  outstr[0]='\0';

  if(val < 0.0){
    strcpy(outstr,"-\0");  //print "-" sign
    val *= -1;
  }

  if( precision == 0) {
    strcat(outstr, ltoa(round(val),temp,10));  //prints the int part
  }
  else {
    unsigned long frac, mult = 1;
    byte padding = precision-1;
    
    while (precision--)
      mult *= 10;

    val += 0.5/(float)mult;      // compute rounding factor
    
    strcat(outstr, ltoa(floor(val),temp,10));  //prints the integer part without rounding
    strcat(outstr, ".\0"); // print the decimal point

    frac = (val - floor(val)) * mult;

    unsigned long frac1 = frac;

    while(frac1 /= 10) 
      padding--;

    while(padding--) 
      strcat(outstr,"0\0");    // print padding zeros

    strcat(outstr,ltoa(frac,temp,10));  // print fraction part
  }

  // generate width space padding 
  if ((widthp != 0)&&(widthp >= strlen(outstr))){
    byte J=0;
    J = widthp - strlen(outstr);

    for (i=0; i< J; i++) {
      temp[i] = ' ';
    }

    temp[i++] = '\0';
    strcat(temp,outstr);
    strcpy(outstr,temp);
  }

  return outstr;
}

Would this be valid documentation for use of this function to add to the top of floatToString.h ?

Please correct where needed.

/*
 *  floatToString.h
 *
 *  Usage: floatToString(buffer string, float value, precision, minimum text width)
 *
 *  Example:
 *  char test[20];    // string buffer
 *  float M;          // float variable to be converted
 *                 // precision -> number of decimal places
 *                 // min text width -> character output width, 0 = no right justify
 * 
 *  Serial.print(floatToString(test, M, 3, 7)); // call for conversion function
 *  
 */

capt.tagon
Good hint making understanding that stuff easier for a beginner!

why not just use avr-gcc's dtostre and dtostrf?

http://www.nongnu.org/avr-libc/user-manual/group__avr__stdlib.html#g6c140bdd3b9bd740a1490137317caa44

Crap frank. ::slight_smile: Didn't know it existed!

Seriously, just use:

#include<stdlib.h>
dtostrf(FLOAT,WIDTH,PRECSISION,BUFFER);

It's 20% smaller than my floatToString(), and probably faster as well.