float/double in sprintf without and with dtostrf

The missing support for using sprintf() with float/double variables is a classic problem. Often the solution is to use dtostrf() or handle the variable in its integer and decimal parts.

Today I wanted to help a friend and make a small demo program showing the issue and the solution. However, much to my surprise this has turned out quite differently than I expected. If the dtostrf() is used then the sprintf() works as “normal”. WOW! Why is that? Below are some sample programs.

Only sprintf()

void setup()
{
    double dblA = 3.14159265;
    char buf[20];

    SerialUSB.begin(9600);
    delay(2000);
    SerialUSB.println("sprintf() demo program");

    int res = sprintf(buf, "%f", dblA);
    SerialUSB.println(buf);
    SerialUSB.println(res);
}

void loop()
{}


OUTPUT
sprintf() demo program

536871600

Only dtostrf()

#include <avr/dtostrf.h>

void setup()
{
    double dblB = 0.31830988;
    char buf[20];

    SerialUSB.begin(9600);
    delay(2000);
    SerialUSB.println("dtostrf() demo program");

    dtostrf(dblB, 0, 8, buf);
    SerialUSB.println(buf);
}

void loop()
{}


OUTPUT
dtostrf() demo program
0.31830988

Both sprintf() and dtostr()

#include <avr/dtostrf.h>

void setup()
{
    double dblA = 3.14159265;
    double dblB = 0.31830988;
    char buf[20];

    SerialUSB.begin(9600);
    delay(2000);
    SerialUSB.println("sprintf() and dtostrf() demo program");

    int res = sprintf(buf, "%f", dblA);
    SerialUSB.println(buf);
    SerialUSB.println(res);

    dtostrf(dblB, 0, 8, buf);
    SerialUSB.println(buf);
}

void loop()
{}


OUTPUT
sprintf() and dtostrf() demo program
3.141593
8
0.31830988

So in conclusion to use sprintf() with float/double variables just make use of dtostrf() once. Or am I confused by something else?

asm(".global _printf_float");

I read somewhere this should fix the problem. Not tested.

It is different on arm boards vs avr boards.

Which Arduino board are you using ?

Arduino Zero, and IDE 1.8.13 on W10 pro

The Arduino Zero has an ARM processor, and evidently, printf() supports floats on that platform.

I had not seen this suggestion, and tried it using an ATmega328 based Arduino. As usual, a “?” is printed instead of the expected value.

asm(".global _printf_float");

Somewhere on the forum, it is explained how to successfully enable the option. A specific link library is required, if I recall correctly.

Well, even on the Arduino Zero sprintf float doesn’t work unconditionally as my examples show. So there must be more to it. I.e. the sprintf has to be “warmed” up with dtostrf first.

The “warmed up” explanation holds no water.

I think you are seeing some other type of error in the first example. On an AVR-based Arduino, the result of using sprintf() to print a float value is always and unambiguously “?”, unless you specifically enable the option.

Hardly anyone mentions the Zero on this forum, so you may not have much luck figuring out what the real problem is.

BTW the one correct format to printf a double value is “%lf”.

Avr has dtostrf() as a sort of alternative to having float support in printf()
Arm doesn’t have it, and instead arduino provides a compatibility hack that uses arm printf() to implement dtostrf (): ArduinoCore-API/dtostrf.c.impl at 76e931e97e378a4a4ba3ea12dd8a09a1b16d1286 · arduino/ArduinoCore-API · GitHub
It uses that asm global thing to pull in the correct version of printf(), apparently.

Is not… c - Correct format specifier for double in printf - Stack Overflow

Post slightly corrected.

I recommend to use %lf in both printf() and scanf(), so as not to forget the latter.

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.