Using the Arduino IDE v1.0.5 and AVR-GCC version 4.8.2 and using the linker options to support floating point numbers (I added that option to my IDE), I can do something like this:
char buffer [64];
double val = 123.456789;
sprintf (buffer, "val is %f\n", val);
Serial.print (buffer);
...and it correctly prints "123.456790"
However, if I try to use the variable precision specifier like this:
sprintf (buffer, "val is %.*f\n", 2, val);
It doesn't work. All it prints is "val is"
However, putting the precision specifier directly inside the line works and, as expected, this:
sprintf (buffer, "val is %.2f\n", val);
...correctly prints this: "val is 123.46"
Am I doing something wrong? I tested my syntax in Linux GCC and it worked fine. Is there maybe a compiler or linker flag I'm missing?
Lastly, please don't tell me to use dtostrf() or any other floating point "workarounds". I need what I described above to work.
econjack:
Which version of the IDE are you using. I'm using 1.5.7 and the line:
sprintf (buffer, "val is %.2f\n", val);
for me produces:
val is ?
Clearly there's some difference between the two.
The Arduino IDE (all of them as far as I know) doesn't link the floating point libraries into the code in order to save memory size.
There is just a little stub that prints a question mark when trying to use a floating point printf specifier.
The floating point code CAN be linked into a compiled sketch if the proper options are placed on the compiler command line.
I edited my IDE code to add an option in Preferences to enable or disable the floating point support at will (see screenshot).
It's handy when I need to print complex lines and don't want to mess with a dozen LCD.print() and dtostrf() calls.
I know it (HORRORS!!!) uses an extra 1.5k of memory, but I have yet to run out and I've done some pretty complex stuff on a little UNO R3 and had memory (program memory) to spare.
Peter_n:
Which version gcc do you use in linux ?
I assume it is not implemented (yet) in the avr-gcc.
I can't find more information on it, and I must admit that I never used the variable precision %.*f
The Linux version:
root@michael:/# gcc -v
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/local/libexec/gcc/x86_64-unknown-linux-gnu/4.9.1/lto-wrapper
Target: x86_64-unknown-linux-gnu
Configured with: ../gcc-4.9.1/configure
Thread model: posix
gcc version 4.9.1 (GCC)
The AVR version:
root@michael:/# avr-gcc -v
Using built-in specs.
COLLECT_GCC=avr-gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/avr/4.8.2/lto-wrapper
Target: avr
Configured with: ../src/configure -v --enable-languages=c,c++ --prefix=/usr/lib --infodir=/usr/share/info --mandir=/usr/share/man --bindir=/usr/bin --libexecdir=/usr/lib --libdir=/usr/lib --enable-shared --with-system-zlib --enable-long-long --enable-nls --without-included-gettext --disable-libssp --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=avr
Thread model: single
gcc version 4.8.2 (GCC)
Version 4.8.2 is fairly recent (release date October 16, 2013). The AVR-GCC docs list the variable format specifier option, so it should be supported...... I would think?
AWOL:
You could write your own format string with another sprintf, but that's a work around, so I won't suggest it.
What I really am trying to do is emulate the built in Print.xxx() syntax for floating point (that is, if I do "Serial.print (123.4567, 2);" I want to get "123.46" as an output, but by using fprintf().
Since I use "fdev_open" to connect Serial to stdin and stdout (and stderr), I use those in my code.
For example, instead of doing something like this:
Serial.print ("Heatsink temp: ");
Serial.print (hstemp, 2);
Serial.print (" deg C");
Serial.println ();
/// and many more of these ridiculous concatenations to display the other sensors
...but sometimes I need to have a variable number of digits precision (i.e. the "2" above) and GCC is supposed to support it (and indeed it DOES support it... but not in the AVR version).
And that is my problem. Of course I could write all kinds of hacks to get the output I want, but then I may as well just do the absurd "Print-Print-Print-Print-Print" sequences for each value I need to print.... and I don't want to do that.
I'm confused, so I made two test programs.
The '' format can also be used with integers, for example for the width.
I tried many things for the Arduino, but I can't make it work. It is not implemented or it is a bug, since the string is always terminated when a format with '' is found.
In linux, file "test.c".
// File test.c
// Compile: gcc test.c -o test
// Run: ./test
// Using: gcc version 4.8.2 (Ubuntu 4.8.2-19ubuntu1)
#include <stdio.h>
int main (void)
{
float f1 = 1.23456789;
int n = 503;
// print 1.234568
printf ("%f \n", f1);
// print 1.23
printf ("%.*f \n", 2, f1);
// print 00503
printf ("%05d \n", n);
// print 000503
printf ("%0*d \n", 6, n);
return 0;
}
For Arduino Uno, the string is terminated when the format has a '*' in it.
// test with variable printf format
// Arduino IDE 1.5.7
char buffer[40];
void setup() {
int n = 503;
Serial.begin(9600);
Serial.println("\n--------");
// print 00503
sprintf(buffer, "%05d", n);
Serial.println(buffer);
// Trying to print 000503, but the number is not printed.
sprintf(buffer, "%s %0*d", "test", 6, n);
Serial.println(buffer);
Serial.println("--------");
}
void loop() {
}
Peter_n:
I'm confused, so I made two test programs.
The '' format can also be used with integers, for example for the width.
I tried many things for the Arduino, but I can't make it work. It is not implemented or it is a bug, since the string is always terminated when a format with '' is found.
The specified width and precision can be at most 255.
Notes:
For floating-point conversions, if you link default or minimized version of vfprintf(), the symbol ? will be output and double argument will be skiped. So you output below will not be crashed. For default version the width field and the "pad to left" ( symbol minus ) option will work in this case.
The hh length modifier is ignored (char argument is promoted to int). More exactly, this realization does not check the number of h symbols.
But the ll length modifier will to abort the output, as this realization does not operate long long arguments. The variable width or precision field (an asterisk * symbol) is not realized and will to abort the output.
So I guess I need to write a little bit of code now....