Go Down

Topic: float to string function (Read 55342 times) previous topic - next topic

tim hirzel

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




zitron

#1
Jan 05, 2009, 08:44 pm Last Edit: Jan 05, 2009, 08:45 pm by zitron Reason: 1
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:

Code: [Select]
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:

Code: [Select]
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

Code: [Select]
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

mem

#2
Jan 05, 2009, 09:25 pm Last Edit: Jan 05, 2009, 09:26 pm by mem Reason: 1
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 [glow]and[/glow] instead of [glow]&&[/glow] . [glow]not[/glow] instead of [glow]![/glow] , I was wondering why this syntax is used?

zitron

Quote
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 [glow]and[/glow] and [glow]not[/glow] 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-

mikalhart

#4
Jan 05, 2009, 10:53 pm Last Edit: Jan 05, 2009, 11:54 pm by mikalhart Reason: 1
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

zitron

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.

Code: [Select]
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;
}

zitron

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

set minimum text width to 0 for no right justify.

Example:

Code: [Select]
#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:

Code: [Select]

...
 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
...

guiambros

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?

halley

Did you read the replies to your previous post yesterday?

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

zitron

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.

Code: [Select]
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;
}

capt.tagon

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

Please correct where needed.

Code: [Select]

/*
*  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
*  
*/

soulid

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

frank26080115

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

http://www.nongnu.org/avr-libc/user-manual/group__avr__stdlib.html#g6c140bdd3b9bd740a1490137317caa44
Freelance engineer, consultant, contractor. Graduated from UW in 2013.

zitron

Crap frank.  ::) 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.

Go Up