Pages: [1]   Go Down
Author Topic: Serial.print() a float/double after sprintf()  (Read 1919 times)
0 Members and 1 Guest are viewing this topic.
Italy
Offline Offline
Jr. Member
**
Karma: 0
Posts: 53
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hi all,

I'm not able to correctly print a char array containing a float (or double) value and obtained with sprintf().

Here is my example sketch:

Code:
void setup()
{
  Serial.begin(115200);
 
  float f = 1.23456;
  char s[10];
  sprintf(s, "%f", f);
  Serial.println(s); 
}

void loop() {}

My output is "?" (a question mark).

I (probably) founded the reason here: http://www.nongnu.org/avr-libc/user-manual/group__avr__stdio.html#gaa3b98c0d17b35642c0f3e4649092b9f1

Quote
Since the full implementation of all the mentioned features becomes fairly large, three different flavours of vfprintf() can be selected using linker options. The default vfprintf() implements all the mentioned functionality except floating point conversions. A minimized version of vfprintf() is available that only implements the very basic integer and string conversion facilities, but only the # additional option can be specified using conversion flags (these flags are parsed correctly from the format specification, but then simply ignored). This version can be requested using the following compiler options:

   -Wl,-u,vfprintf -lprintf_min

If the full functionality including the floating point conversions is required, the following options should be used:

   -Wl,-u,vfprintf -lprintf_flt -lm

Question 1: Can I introduce the full vfprintf() functionality within the Arduino IDE?

Question 2: If the answer to the previous question is "no", how can I convert a float/double into a char array?
Logged

Italy
Offline Offline
Jr. Member
**
Karma: 0
Posts: 53
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Ok, founded this:

http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1205038401/12#12
Logged

Ontario
Offline Offline
God Member
*****
Karma: 20
Posts: 835
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

how can I convert a float/double into a char array?

I would have thought there should be an AVR LIBC function to do this without sprintf(), but I don't see it.

.../arduino-0022/hardware/arduino/cores/arduino/Print.cpp does it the hard way.  Maybe just take that code and munge it to do what you need:

Code:
void Print::printFloat(double number, uint8_t digits)
{
  // Handle negative numbers
  if (number < 0.0)
  {
     print('-');
     number = -number;
  }

  // Round correctly so that print(1.999, 2) prints as "2.00"
  double rounding = 0.5;
  for (uint8_t i=0; i<digits; ++i)
    rounding /= 10.0;
 
  number += rounding;

  // Extract the integer part of the number and print it
  unsigned long int_part = (unsigned long)number;
  double remainder = number - (double)int_part;
  print(int_part);

  // Print the decimal point, but only if there are digits beyond
  if (digits > 0)
    print(".");

  // Extract digits from the remainder one at a time
  while (digits-- > 0)
  {
    remainder *= 10.0;
    int toPrint = int(remainder);
    print(toPrint);
    remainder -= toPrint;
  }
}
Logged

Italy
Offline Offline
Jr. Member
**
Karma: 0
Posts: 53
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hi gardner,

the AVR dtostrf() function does the trick.

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

Left Coast, USA
Offline Offline
Sr. Member
****
Karma: 5
Posts: 499
Sometimes I just can't help myself.
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

...
I would have thought there should be an AVR LIBC ...

dtostrf() does the deed for "straight" floating point, and dtostre() (just above the dtostrf() in the link that ramo102 gave) does it for scientific notation.   (That link was also given in the thread that ramo102 had previously shown us.)
Code:
// Demo of avr-libc dtostrf() and dtostre()
//
// davekw7x

void setup()
{
    Serial.begin(115200);

    float x = -1.23456;
    byte precision = 4;  // Number of digits after the decimal point

    // Make room for your floating point number, however you
    // plan to format it.
    char floatBuffer[20];

    // Make room for entire print line, however long you plan
    // to make it.
    char printBuffer[80];

    // Width of floating point field includes places for
    //
    // The sign
    // One digit before the decimal point
    // The decimal point
    // Digits after decimal point (== precision)
    // (Don't forget that the buffer has to hold the
    //  terminating zero byte.)

    // Bottom line: The size of floatBuffer must be at least
    // precision+4.  The "width" field in the call do
    // dtostrf should be at least precision plus 3 and at most
    // the size of floatBuffer minus 1
    //
    dtostrf(x, precision+3, precision, floatBuffer);

    // You could just print the raw value.  (You might
    // have other Serial.print() statements to give it some
    // user-friendly context.)
    Serial.println(floatBuffer);


    // Or you could use sprintf to create an entire line in a
    // different buffer.  Then you could use Serial.print
    // or LiquidCrystal::print, or whatever, to emit
    // the whole enchilada in one statement.
    sprintf(printBuffer, "With %%.%df precision, x = %s", precision, floatBuffer);
    Serial.println(printBuffer);


    // Width of scientific field includes places for
    //
    // The sign.
    // One decimal digit before the decimal point.
    // The decimal point.
    // Some digits ofter the decimal point (== precision).
    // The 'e' character for exponent.
    // The sign of the exponent.
    // Two places for the exponent value.
    // (Don't forget that the buffer has to hold the
    //  terminating zero byte.)

    // Bottom line: The size of floatBuffer must be at least
    // precision + 8
    //

    // Print out just the value
    dtostre(x, floatBuffer, precision, NULL);
    Serial.println(floatBuffer);

    // Print out the value with some other stuff from sprintf
    sprintf(printBuffer,"With %%.%de precision, x = %s", precision, floatBuffer);
    Serial.println(printBuffer);

}

void loop()
{
}

Output:


-1.2346
With %.4f precision, x = -1.2346
-1.2346e+00
With %.4e precision, x = -1.2346e+00



By converting to a C-style"string" in an array rather than printing it within some function, you can send it to an LCD or over a network connection, or do whatever you usually do with "strings."


Regards,

Dave

Footnote:
These functions also handle special non-number values like "inf" that can occur with floating point overflow or things like "not a number" that can occur in certain operations...

Change the example to something like:
Code:
.
.
.
    float x = -1.23456;
    float y = x / 0.0;
.
.
.
    dtostrf(y, precision+3, precision, floatBuffer);
.
. // Use Serial.print or sprintf, or whatever, with this value
.
    dtostre(y, floatBuffer, precision, NULL);
.
. // Use Serial.print or sprintf, or whatever, with this value
.

And you can see stuff like

With %.4f precision, x =    -INF

« Last Edit: June 29, 2011, 01:42:43 pm by davekw7x » Logged

Pages: [1]   Go Up
Jump to: