I know the arduino core libraries (maybe even the “std” libraries) are not 100% perfect, thus the printf() function can’t not handle float values. This is mainly due to va_start, va_list … va_end, not being the same as you would find if you use a real C/C++ compiler.
Well, I want to try adding float values to printf(), or at least give it a shot. However I can’t not find where the Arduino software developers hid the actual __builtin_va_start … end functions. Are they somewhere in the library folders or is it part of the GCC files?
I made this sketch to convert a floating value to a string. I can also change it to the standard format of “%4.2f” down the line, that’s not a problem. But where do I start?
/*
Had to make my own pow() function as the standard pow() has a return type of double,
and for some reason type casting from a double to an int produces 1 less that of the value.
Ex. int(pow(10, 3)) returns 999 instead of 1000. (using IDE 1.6.0 on an UNO)
*/
void setup()
{
// put your setup code here, to run once:
Serial.begin(115200);
char myB[10]; // buffer
ftos(myB, 4, 3, 1234.567);
Serial.println(myB);
}
void loop() {
// put your main code here, to run repeatedly:
}
void ftos(char * buf, byte W, byte D, float data)
{
long Wdata = data, tmp;
float dec = data - long(data);
// get the first whole number and convert it
buf[0] = Wdata / Pow(10, W - 1) + '0';
tmp = Wdata % Pow(10, W - 1);
//now get the rest of the whole numbers
for (byte i = 1; i < W; i++)
{
long factor = Pow(10, W - 1 - i);
buf[i] = (tmp / factor) + '0';
tmp %= factor;
}
buf[W] = '.'; // add the decimal point
// now do the decimal numbers
for (byte i = 0; i < D; i++)
{
dec *= 10;
buf[W + i + 1 ] = (long(dec) % 10) + '0';
}
// don't forget the NULL terminator
buf[W + D + 1] = NULL;
}
unsigned long Pow(long V, byte shift)
{
unsigned long Val = 1;
while(shift-- != 0)
Val *= V;
return Val;
}
HazardsMind:
I know the arduino core libraries (maybe even the “std” libraries) are not 100% perfect, thus the printf() function can’t not handle float values. This is mainly due to va_start, va_list … va_end, not being the same as you would find if you use a real C/C++ compiler
Where did you get that idea? The Arduino compiler is just an AVR version of the popular GCC compiler. There is absolutely nothing wrong with it, and it certainly can handle floating point functions.
Problem is the people who developed the Arduino IDE decided to leave out the floating point support. It’s simply a matter of the IDE not generating the proper GCC compile and link strings to send to the compiler.
The reason the FP code is not linked in is because it takes up about 2K of space, and we don’t want people with a 32K microcontroller to write a 5K program and then use another 2.5K for floating point. No, sir! We have to keep as much flash memory unused as possible!
The IDE want from version 1.0 to 1.06 to what now? 1.6.5 and still the requests for floating point support (and printf_P support) have gone unheard.
It takes literally less than 10 minutes to write a few extra lines of code to add an OPTION in the IDE to use or not use floating point.
Can you imagine how much torn out hair would finally be growing back by now if people could just click [ ] Floating Point instead of wondering what the darn question mark means and discovering that it’s actually NOT their fault or a mistake in programming?
(sense my frustration you can, mmmmmm?) ← Yoda voice
Yes, but you can use dtostrf if you want to print floats. People who are already struggling with program size won't want the extra 2 KB included, if they don't even print floats ever.
The option idea might be one work-around, but then you get people complaining that the code you posted "doesn't work" because you have the option checked and they don't.
Honestly… when has someone ever struggled with program size? I know a lot of people THINK they are “out of memory” due to having strings in SRAM instead of PROGMEM, but who ever runs out of FLASH?
Not to mention the tricky syntax that dtostrf uses.
Krupski:
Honestly... when has someone ever struggled with program size? I know a lot of people THINK they are "out of memory" due to having strings in SRAM instead of PROGMEM, but who ever runs out of FLASH?
I am very nearly out of FLASH on one of my products running on a ProMini, and there is very little left that can be optimized further. There is just a LOT of functionality in there. It's not at all rare, or even unusual.
pYro_65:
If you have 1.6.5 and above, you can download my PrintEx library from the library manager.
Or it can be manually installed in versions 1.5.x and up.
Among other things, it includes printf with floating point support.
#include <PrintEx.h>
PrintEx serial = Serial;
void setup(){
Serial.begin(9600);
serial.printf("/%31n\\n| A floating point number: %f |\n\%31n/", ‘=’, 3.14f, ‘=’ );
}
void loop() {}
Produces:
/===============================
| A floating point number: 3.14 |
===============================/
The readme has an overview of its usage.
[https://github.com/Chris--A/PrintEx](https://github.com/Chris--A/PrintEx#printex-library-for-arduino-)
fprintf (stdout, "I like cherry PI! %.2f\n", M_PI);
RayLivingston:
I am very nearly out of FLASH on one of my products running on a ProMini, and there is very little left that can be optimized further. There is just a LOT of functionality in there. It’s not at all rare, or even unusual.
Regards,
Ray L.
I’m not saying it NEVER happens (running out of flash). I’m just sure it’s quite rare… and that’s the point of having the FP library linkage as an OPTION… to turn it off if you need the last byte of space.
Krupski:
Is a selectable preference REALLY so hard to use?
No, if people know they exist. I have done a lot of support for software where people ask how do you do so-and-so and you say "check the box in the configuration dialog" and they reply "what? there's a configuration dialog"?
/*
Had to make my own pow() function as the standard pow() has a return type of double,
and for some reason type casting from a double to an int produces 1 less that of the value.
Ex. int(pow(10, 3)) returns 999 instead of 1000. (using IDE 1.6.0 on an UNO)
*/
void setup()
{
// put your setup code here, to run once:
Serial.begin(115200);
char myB[10]; // buffer
//ftos(myB, 4, 3, 1234.567); // 450 bytes IDE 1.6.0 using Uno R3
dtostrf(1234.567, 4, 3, myB); // 2,028 bytes IDE 1.6.0 using Uno R3
Serial.println(myB);
}
void loop() {
// put your main code here, to run repeatedly:
}
char * ftos(char * buf, byte W, byte D, float data)
{
byte shf = 0;
if (data < 0)
{
data *= -1;
shf = 1;
buf[0] = '-';
}
long Wdata = data, tmp;
float dec = data - long(data);
// get the first whole number and convert it
buf[0 + shf] = Wdata / Pow(10, W - 1) + '0';
tmp = Wdata % Pow(10, W - 1);
//now get the rest of the whole numbers
for (byte i = 1; i < W; i++)
{
long factor = Pow(10, W - 1 - i);
buf[i + shf] = (tmp / factor) + '0';
tmp %= factor;
}
buf[W + shf] = '.'; // add the decimal point
// now do the decimal numbers
for (byte i = 0; i < D; i++)
{
dec *= 10;
buf[W + i + 1 + shf] = (long(dec) % 10) + '0';
}
// don't forget the NULL terminator
buf[W + D + 1 + shf] = NULL;
return buf;
}
unsigned long Pow(long V, byte shift)
{
unsigned long Val = 1;
while (shift-- != 0)
Val *= V;
return Val;
}
/*
Had to make my own pow() function as the standard pow() has a return type of double,
and for some reason type casting from a double to an int produces 1 less that of the value.
Ex. int(pow(10, 3)) returns 999 instead of 1000. (using IDE 1.6.0 on an UNO)
*/
void setup()
{
// put your setup code here, to run once:
Serial.begin(115200);
char myB[10]; // buffer
char myB2[10];
unsigned long current = micros();
ftos(-1234.567, 4, 3, myB);
Serial.println(micros() - current);
delay(1);
current = micros();
dtostrf(-1234.567, 4, 3, myB2);
Serial.println(micros() - current);
Serial.println(myB);
Serial.println(myB2);
}
void loop() {
// put your main code here, to run repeatedly:
}
char * ftos(float data, byte W, byte D, char * buf)
{
byte shf = 0;
if (data < 0)
{
data *= -1;
shf = 1;
buf[0] = '-';
}
long Wdata = data, tmp;
float dec = data - long(data);
// get the first whole number and convert it
buf[0 + shf] = Wdata / Pow(10, W - 1) + '0';
tmp = Wdata % Pow(10, W - 1);
//now get the rest of the whole numbers
for (byte i = 1; i < W; i++)
{
long factor = Pow(10, W - 1 - i);
buf[i + shf] = (tmp / factor) + '0';
tmp %= factor;
}
buf[W + shf] = '.'; // add the decimal point
// now do the decimal numbers
for (byte i = 0; i < D; i++)
{
dec *= 10;
buf[W + i + 1 + shf] = (long(dec) % 10) + '0';
}
// don't forget the NULL terminator
buf[W + D + 1 + shf] = NULL;
return buf;
}
unsigned long Pow(long V, byte shift)
{
unsigned long Val = 1;
while (shift-- != 0)
Val *= V;
return Val;
}
Check out my Arduino library for providing printf:
All you do is either make a new directory in "libraries" named "Stdinout" and copy the .cpp and .h files into it, or "install" the library using the IDE.
Now, say you want to use printf with the serial port. After opening the serial port as usual, simply add the line
Stdio.open (Serial);
to your code. Of course, you first need to #include "Stdinout" in your sketch!
Note that this only provides stdin/out/err support - it does not give you floating point.