Pages: [1]   Go Down
Author Topic: float to string function  (Read 36573 times)
0 Members and 2 Guests are viewing this topic.
0
Offline Offline
Newbie
*
Karma: 0
Posts: 37
Arduino rocks
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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



Logged

0
Offline Offline
Full Member
***
Karma: 1
Posts: 222
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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:
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:
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:
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
« Last Edit: January 05, 2009, 02:45:56 pm by zitron » Logged

London
Offline Offline
Faraday Member
**
Karma: 8
Posts: 6240
Have fun!
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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?
« Last Edit: January 05, 2009, 03:26:47 pm by mem » Logged

0
Offline Offline
Full Member
***
Karma: 1
Posts: 222
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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

Austin, TX USA
Offline Offline
God Member
*****
Karma: 4
Posts: 997
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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
« Last Edit: January 05, 2009, 05:54:27 pm by mikalhart » Logged

0
Offline Offline
Full Member
***
Karma: 1
Posts: 222
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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  smiley-grin). Maybe it can be more efficient, but I think it's pretty good.

Code:
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;
}
Logged

0
Offline Offline
Full Member
***
Karma: 1
Posts: 222
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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

set minimum text width to 0 for no right justify.

Example:

Code:
#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:

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

0
Offline Offline
Newbie
*
Karma: 0
Posts: 5
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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, smiley-cool);
  Serial.println(floatToString(str, 32766.6613, 4, smiley-cool);
  Serial.println(floatToString(str, 1.6613, 4, smiley-cool);
  Serial.println(floatToString(str, -32766.1234, 2, smiley-cool);
  Serial.println(floatToString(str, -32766.1234, 4, smiley-cool);

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?
Logged

Connecticut, US
Offline Offline
Edison Member
*
Karma: 2
Posts: 1036
Whatduino
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Did you read the replies to your previous post yesterday?

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

0
Offline Offline
Full Member
***
Karma: 1
Posts: 222
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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:
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;
}
Logged

Orygun
Offline Offline
Full Member
***
Karma: 0
Posts: 195
Don't let the smoke out!
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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

Please correct where needed.

Code:
/*
 *  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
 *  
 */
Logged

0
Offline Offline
Full Member
***
Karma: 1
Posts: 140
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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

0
Offline Offline
God Member
*****
Karma: 0
Posts: 594
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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

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

Freelance engineer, consultant, contractor. Graduated from UW in 2013.

0
Offline Offline
Full Member
***
Karma: 1
Posts: 222
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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

Pages: [1]   Go Up
Jump to: