Hi! I am using the sprintf function to construct a string that will consist of multiple floating point values. According to this thread the Arduino environment does not support floating point input in sprintf: http://arduino.cc/forum/index.php/topic,44262.0.html, but the thread does link to some methods that convert floating point values into strings that can be concatenated in sprintf. In this thread, there is code to do such a thing (http://arduino.cc/forum/index.php/topic,44216.0.html#11) but this code is from an older version of Arduino. Is there a more efficient way to create a string out of floating point values in 1.02? Thanks!
Is there a more efficient way to create a string out of floating point values in 1.02?
What are you intending to do with the string? In most cases where people think they need a string, they don't really.
No, there isn't a more efficient way.
Well, whenever I try to use floating point values with sprintf (ie: sprintf(buffer, "The number is %f", f_val1)) I get a question mark as output. I looked this up online, found the first thread that I linked to, and therefore was led to believe that converting it into a string would be easier. Is there an alternate way?
For enabling floats in sprintf (and sscanf) you can install this in \hardware\tools\avr\avr\lib (make a backup of that lib folder, and then extract and replace files): Arduino Forum
Another option is to use dtostrf (I believe it's more efficient, and use much less memory than sprintf with FP enabled): avr-libc: <stdlib.h>: General utilities
Thank you, dtostrf works like a charm!
Curious here, ow do you enable FP? Thanks.
Go in /hardware/tools/avr/avr/, here you have a lib folder: make a copy (or a zip) of it, as a backup.
Download the file I linked, open it, select all the files in it and drag and drop in that lib folder. It will ask you to replace files and folders, click Yes to All and it's ready, now you can use %f etc in sprintf and sscanf
Is there an alternate way?
As you've seen, yes. However, you failed to describe why you need a string. Most output classes (EthernetClient, HardwareSerial, File, etc.) derive, at least indirectly, from Print, which knows how to output a float.
There are very few cases where converting all the data to be output to a string, and outputting the whole string at once is necessary.
And, as you've been made aware, doing so incurs a cost.
Is there an alternate way?
sprintf() is very inefficient and should be avoided as much as possible.
The typical way to convert a float is to break it to its integer and fraction portions and then convert each portion, as integer, separately.
mrShrimp:
Hi! I am using the sprintf function to construct a string that will consist of multiple floating point values. According to this thread the Arduino environment does not support floating point input in sprintf: http://arduino.cc/forum/index.php/topic,44262.0.html, but the thread does link to some methods that convert floating point values into strings that can be concatenated in sprintf. In this thread, there is code to do such a thing (http://arduino.cc/forum/index.php/topic,44216.0.html#11) but this code is from an older version of Arduino. Is there a more efficient way to create a string out of floating point values in 1.02? Thanks!
If you like, you can "patch" your Arduino libraries to include the floating point code. The standard "libc.a" is put together with the "min" (minimal) versions of sprintf and sscanf to save space. If you remove the minimal libraries and replace them with the floating point libraries, then sprintf and sscanf work properly with floating point.
This does, however, add about 1.5K to your final code size. I've written quite large and elaborate programs and barely used more than 16K of program memory, so I think the worry over an extra 1.5K is, in most cases, pointless.
If you wish to patch your libraries, use this script:
#!/bin/bash
################################################################################
# fixfp - script to install floating point support into Arduino printf library
#
# For more information, see this post:
# http://arduino.cc/forum/index.php/topic,124809.msg938573.html#msg938573
################################################################################
STATUS=0
## Exit if libc.a isn't here
test -e libc.a
if [ ${?} -ne 0 ]; then {
echo "File 'libc.a' not found - exiting"
exit 0
} fi
test -e libc.a.orig
let STATUS+=${?}
test -e vfprintf_flt.o
let STATUS+=${?}
test -e vfscanf_flt.o
let STATUS+=${?}
if [ $STATUS -eq 0 ]; then {
echo "Floating point patch already performed - exiting"
exit 0
} else {
cp libc.a libc.a.orig
ar -dv libc.a vfprintf_std.o
ar -dv libc.a vfscanf_std.o
ar -xv libprintf_flt.a vfprintf_flt.o
ar -xv libscanf_flt.a vfscanf_flt.o
ar -rv libc.a vfprintf_flt.o
ar -rv libc.a vfscanf_flt.o
echo "Floating point patch installed."
} fi
If you use Windows, then the easiest thing to do is simply replace the libraries via copying. Although the Arduino IDE code differs between Windows, Linux and MACOS, the AVR libraries are all the same. If you want to do this, find where your "libc.a" files are (there are several), then unzip the attached file into that directory. It will over-write the "libc.a" files with the floating point versions.
Unzipping the file will replace your libc.a files and also add "libc.a.original" which is a backup copy of the original libraries in case you want to revert back.
Hope this helps.
(edit to add): See this post for more info on Windows installation of the libc.a files: Strange problem with "sprintf" - #18 by guix - Programming Questions - Arduino Forum
floating_point_libc.zip (1.83 MB)
dhenry:
Is there an alternate way?
sprintf() is very inefficient and should be avoided as much as possible.
The typical way to convert a float is to break it to its integer and fraction portions and then convert each portion, as integer, separately.
How is sprintf "inefficient"? Takes up a lot of code space? Too slow? What's wrong with it?
Honestly, I find it a LOT easier to make up a simple template, then "sprintf" the variables into the string and finally just print the buffer as opposed to a whole bunch of "Serial.print" or "LCD.print" lines strung together to "construct" a line.
Also, if I want to print a floating point number, the xxx.print() code gives me 2 decimal places (i.e. "12.34"). What if I want more or less?
It's much easier (for me at least) to do it the standard C way and say "%5.2f" (or whatever) and specify exactly how my output will look.
Krupski:
Honestly, I find it a LOT easier to make up a simple template, then "sprintf" the variables into the string and finally just print the buffer as opposed to a whole bunch of "Serial.print" or "LCD.print" lines strung together to "construct" a line.
That is what I do too, format and print. That saves lots of space from not having to include overloaded .print(int), .print(float) etc.
dhenry:
sprintf() is very inefficient and should be avoided as much as possible.The typical way to convert a float is to break it to its integer and fraction portions and then convert each portion, as integer, separately.
Example code of both methods, please. Plus metrics to demonstrate that your statement is correct that sprintf is less efficient than "doing it yourself".
Some test code:
volatile int num = 42;
const int ITERATIONS = 10000;
void setup ()
{
Serial.begin (115200);
} // end of setup
void loop ()
{
char buf [10];
int i;
unsigned long start, finish;
start = micros ();
for (i = 0; i < ITERATIONS; i++)
sprintf (buf, "%i", num);
finish = micros ();
Serial.print ("Result of sprintf = ");
Serial.println (buf);
Serial.print ("Time taken = ");
Serial.println (finish - start);
start = micros ();
for (i = 0; i < ITERATIONS; i++)
itoa (num, buf, 10);
finish = micros ();
Serial.print ("Result of itoa = ");
Serial.println (buf);
Serial.print ("Time taken = ");
Serial.println (finish - start);
delay (1000);
} // end of loop
Results:
Result of sprintf = 42
Time taken = 574696
Result of itoa = 42
Time taken = 314572
itoa executed in 54% of the time. I don't know about "very inefficient". They do different things.
sprintf can concatenate literals, and various data types into a single string in one function call.
Even the slower sprintf took only 57 uS to execute once (574696 / 10000). It takes 87 uS to send a single byte out the serial port at 115200 baud. So the overhead of doing the sprintf isn't that great, compared to the overhead of transmitting the results.
If you have a very specific case (eg. the number is in the range 0 to 9) then of course you can make a fast, efficient conversion.
sprintf is really your "all purpose" converter.
However since it doesn't handle floats on the Arduino, saying not to use it because it is inefficient is missing the point. Don't use for a more obvious reason: it doesn't work.
Also, if I want to print a floating point number, the xxx.print() code gives me 2 decimal places (i.e. "12.34"). What if I want more or less?
There is an optional second argument for some of the print overloads. For floats and doubles, the optional second argument tells it how many decimal places you want to see.