AVR scanf() not working with floats?

I am using an Arduino Mega 2560 R3. I typically use ARM chips of various flavors, but for this particular one-off prototype I am using the Mega because of the large number of analog inputs and digital I/O. Note that with ARM development, I have no problems with printf() and scanf().

I have modified the platform.txt file to include "-Wl,-u,vfprintf -lprintf_flt -lm" when compiling and linking. This adds support for floating point values in the printf and scanf family.

Here is a simple example.

void setup() {
    float f = 6.66;
    double d = 9.99;
    char msg[100];

    snprintf(msg, sizeof(msg), "printf float  into f  %f",  f);
    snprintf(msg, sizeof(msg), "printf float  into lf %lf", f);
    snprintf(msg, sizeof(msg), "printf double into f  %f",  d);
    snprintf(msg, sizeof(msg), "printf double into lf %lf", d);

    sscanf("9.99", "%f",  &f);
    sscanf("9.99", "%lf", &f);
    sscanf("9.99", "%f",  &d);
    sscanf("9.99", "%lf", &d);
}

void loop() {
}

When I compile with "-wall", the output includes:

arduino-cli compile --fqbn arduino:avr:mega --verbose --warnings all Compile


AVR-Compiler-Tests/Compile/Compile.ino:7:59: warning: format '%f' expects argument of type 'double', but argument 4 has type 'float' [-Wformat=]
     snprintf(msg, sizeof(msg), "printf float into f %f", f);
                                                           ^
AVR-Compiler-Tests/Compile/Compile.ino:8:61: warning: format '%lf' expects argument of type 'double', but argument 4 has type 'float' [-Wformat=]
     snprintf(msg, sizeof(msg), "printf float into lf %lf", f);
                                                             ^
AVR-Compiler-Tests/Compile/Compile.ino:13:29: warning: format '%lf' expects argument of type 'double*', but argument 3 has type 'float*' [-Wformat=]
     sscanf("9.99", "%lf", &f);
                           ~~^
/Users/jwright/src/nemo.SVN/tray/Development/Compile.ino:14:28: warning: format '%f' expects argument of type 'float*', but argument 3 has type 'double*' [-Wformat=]
     sscanf("9.99", "%f", &d);
                          ~~^

The messages for sscanf() are what I expect - %lf should be used with double and %f should be used with float.

The messages for snprintf() seem broken to me. A format of %f expects double, and a format of %lf expects double. Huh?

Thanks,
Jim

Not so much broken as pedantic, perhaps (I'm feeling generous tonight). And definitely "huh?" inducing if you're not one of those folks that enjoy reading language standards.

I believe this is the same, or much the same, issue. From 2006.

Default argument promotions can be a bit headache inducing at times.

https://stackoverflow.com/questions/44421125/better-understanding-type-promotion-of-variadic-parameters-in-c

Thanks! I had previously checked to see that both float and double are implemented but I misread my results. Now I see that both float and double are 4 bytes on AVR, so float seems to be the same thing as double. I am not sure exactly what is going on in the compiler, but I think I can just accept this warning.

(And I'll be reading up on default argument promotions. Something new to learn. :slight_smile: )

1 Like

I am using an Arduino Mega 2560 R3. I typically use ARM chips of various flavors, but for this particular one-off prototype I am using the Mega because of the large number of analog inputs and digital I/O. Note that with ARM development, I have no problems with printf() and scanf().

I have modified the platform.txt file to include "-Wl,-u,vfprintf -lprintf_flt -lm" when compiling and linking. This adds support for floating point values in the printf and scanf family.

Here is a simple example.

void setup() {
    int i = -1;
    double d = -2.0;
    float f = -3.0;
    int res;
    char msg[100];

    Serial.begin(115200);
    while(!Serial) { /* wait for serial port */ }
    Serial.println(); Serial.println();

    res = sscanf("7", "%d", &i);
    Serial.print("Result is: "); Serial.println(res);
    Serial.print("i is: ");      Serial.println(i);

    res = sscanf("6.66", "%f", &f);
    Serial.print("Result is: "); Serial.println(res);
    Serial.print("f is: ");      Serial.println(f);

    res = sscanf("9.99", "%lf", &d);
    Serial.print("Result is: "); Serial.println(res);
    Serial.print("d is: ");      Serial.println(d);

    res = snprintf(msg, sizeof(msg), "int %d float %0.1f double %0.2lf", i, f, d);
    Serial.print("Result is: "); Serial.println(res);
    Serial.print("msg is: ");    Serial.println(msg);
}

void loop() {
}

And here is the output.

Result is: 1
i is: 7
Result is: 0
f is: -3.00
Result is: 0
d is: -2.00
Result is: 29
msg is: int 7 float -3.0 double -2.00

I can scanf() an int value. But I cannot scanf() a float or a double. I can use int and float and double in printf(), so I think that shows that I am using the proper library with full scanf/printf support for floats and double. But scanf() of float isn't working.

I must be doing something wrong, but I cannot see the problem.

Thanks,
Jim

It is not supported by default, and variable type double is not supported on any Arduino AVR MCU (double is treated exactly like float).

Instead of scanf(), input an ASCII character string and use atof() to convert to float.

Edit: please do not cross-post.

I know floats are not supported by default. I believe I have done the steps to enable support.

Changing from scanf() to atof(), or any of numerous other alternatives, would involve a lot of work re-writing code that works fine on other platforms. I'm trying to make use of the AVR library that is supposed to support this, but I'm encountering problems.

I previously checked but misread my results. It does look like float and double are both 4 bytes on AVR. Thanks for that.

I posted two separate but related issues.

I am still trying to understand why scanf() doesn't work when I am using the library that is supposed to support this.

@jrw429 ,

I realise you started 2 topics in good faith but I've merged them as they are basically the same question about different functions.

Thanks Perry, understood. I changed the subject line to reference the main issue I am trying to resolve.

I am not certain this is the code for the avr-libc library used by the Arduino IDE, but I'm guessing it is. And the source for scanf() and friends is vfscanf.c. Looking at that, I do not see any reason for sscanf() to not work on floats. So it feels like I am doing something wrong in my code, but I can't spot it.

An interesting but unrelated point. The AVR has 16-bit pointers for accessing program memory (flash), using a "load program memory" instruction. But if the device has more than 64K of flash, it uses 24-bit pointers and has an "extended load program memory" instruction.

I believe that those functions (and certainly type double) are supported out of the box in Arduino, when using more modern processors, which are also much faster, far more flexible and have vastly larger amounts of memory, at roughly the same cost.

The ~35 year old AVR processor is a nice one for beginners to learn on: the recent ATmega328pb data sheet has only 406 pages, but Is it worth your effort to continue down this very limited, dead end alley?

I need 16 analog inputs. And to use a particular piece of test equipment I need a board running at 5V. We will eventually develop a custom circuit board with an ARM microcontroller and probably use four or five TI ADS1115 chips. But for early prototype testing I want something quick and easy. The 16 analog inputs on the Arduino Mega 2560 R3 seemed like an easy way to accommodate the analog inputs, and it also has plenty of digital I/O. I had a couple boards on hand. Saves me from soldering a protoboard and all the complicated code to deal with the ADS1115 and doing voltage conversion from 3.3V to 5V to interface to the test equipment. This is a one-off test to support hardware development. The I/O aspect is a good fit, but the 8-bit AVR microcontroller has been a learning curve. Learning is good.

And... I have solved the problem. It seems the vast majority of references only discuss the printf side of things, and do not consider the scanf. This question on StackOverflow provides the answer.

All the references I found had said to add this to enable support for floats.

-Wl,-u,vfprintf -lprintf_flt -lm

That is correct for printf, but does not enable floats for scanf. The full solution is to use this.

 -Wl,-u,vfprintf -lprintf_flt -Wl,-u,vfscanf -lscanf_flt -lm

With that, using sscanf() to read floats works.

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