Skip to content

sprintf, sscanf is not correctly implemented in stdio.h #1719

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
dgrat opened this issue Dec 5, 2013 · 38 comments
Closed

sprintf, sscanf is not correctly implemented in stdio.h #1719

dgrat opened this issue Dec 5, 2013 · 38 comments
Labels
Component: Core Related to the code for the standard Arduino API Type: Bug

Comments

@dgrat
Copy link

dgrat commented Dec 5, 2013

Float operations don't work with these functions. Instead a zero is inserted. Integers do work.

@nickgammon
Copy link

I believe that is by design because of the substantial extra space required for such features. Since sscanf and sprintf need to decode their formatting strings at runtime, the compiler can not optimize away unneeded code. Implementing this change would make everyone's code larger, bearing in mind program memory is usually in short supply.

You can scan floats by scanning a string and then applying atof on them. You can print floats by using dtostrf.

@Lauszus
Copy link
Contributor

Lauszus commented Dec 20, 2013

@dgrat you should see this page for details: http://www.nongnu.org/avr-libc/user-manual/group__avr__stdio.html#gaa3b98c0d17b35642c0f3e4649092b9f1.

You can enable it by using the compiler options described on the page.

printf:

-Wl,-u,vfprintf -lprintf_flt -lm

scanf:

-Wl,-u,vfscanf -lscanf_flt -lm

@dgrat
Copy link
Author

dgrat commented Dec 21, 2013

Thx

2013/12/20 Kristian Sloth Lauszus [email protected]

@dgrat https://github.com/dgrat you should see this page for details:
http://www.nongnu.org/avr-libc/user-manual/group__avr__stdio.html#gaa3b98c0d17b35642c0f3e4649092b9f1
.

You can enable it by using the compiler options described on the page:

-Wl,-u,vfprintf -lprintf_flt -lm


Reply to this email directly or view it on GitHubhttps://github.com//issues/1719#issuecomment-31047589
.

@shiftleftplusone
Copy link

shiftleftplusone commented Feb 26, 2015

I don't think this is a good idea.
standard functions (like provided by stdio.h) are expected to work just as the standard!

If anyone is supposed to exclude standards then he is expected to exclude them by himself.
But AT LEAST anyone who wants these standard features should be able to simply INCLUDE the standards by something like:

#pragma config noeffingformatbugs

or whatever !

I got functions like:

sprintf(sdata,"%08d %08d %08.3f %08.3f", p, q, x, y);
cnt = sscanf (sdata,"%08d %08d %08.3f %08.3f", &m, &n, &v, &w);

I'm using them by an Arduino Due,
and I want them to work authoritatively !

who wants stdio.h by #include... shall get stdio.h

and who doesn't want these features, shall leave it off or shall use a crippledio.h by:

#include <crippledio.h>

instead!

@nickgammon
Copy link

Well, not really on a platform like the Arduino Uno.

Where is stdin, stdout, stderr? What about FILE pointers?

It's reasonable to provide a cut-down version for a machine that has 2 kB of RAM and 32 kB of programmable memory.

Maybe on the Due, I can't speak for that.

@shiftleftplusone
Copy link

I'm not talking about Unos! Arduino is not just equal to Unos or Nanos!
I have 2 Megas and 2 Dues (and urgently am waiting for my Tres) and I got lot of data (100k - 1 GB) to be stored (preferably by fprintf and fflush !) and read (preferably by fscanf and sscanf !) and diplayed on TFTs (preferably by sprintf)

  • and I actually hate C compilers which don't apply to C standard libs - such as stdio.h and stdlib.h !!

Acting from necessity I finally already had to write my homebrewed fgets() by my own - that is really unreasonable and unconscionable!

So please provide all standard libs as a default (gpp already provides them all !!), optionally to be #included as usual (anyway, who doesn't need it woudn't need to #include it !),
and maybe provide additional mutilated libs for micro-AVRs!

And please before the last day'll come - give me workarounds to utilize float formatters for sscanf !!!

@matthijskooijman
Copy link
Collaborator

I actually think that at least printf is already available on AVR / Arduino. Not sure about scanf. But you didn't answer the essential question yet:

Where is stdin, stdout, stderr? What about FILE pointers?

If you want to use printf, you'll have to tell it where to send its data. libc allows you to assign the "stdout" variable to a custom stream, created with e.g. FDEV_SETUP_STREAM. See for example: https://github.com/erictj/uracoli/blob/master/wibo/wibo.c#L146-L161 and https://github.com/erictj/uracoli/blob/master/wibo/wibo.c#L237.

Admittedly, Arduino could make it easier to e.g. select "Serial" as the target for printf. However, printf is not a very friendly and easy API for novice users, which is Arduino's primary target audience. If you want to use it, you're welcome to, but it's up to you to set it up.

Going back to the original question, which was about floats, not printf itself, as mentioned that needs link-time options, that will probably not be enabled by default due to the space overhead. Until there is a decent mechanism for specifying compiler flags in a sketch, there's not much that can be done there.

As for the Due, I wouldn't be surprised if sprintf'ing floats actually works there. It's disabled by avr-libc by default, not Arduino, so I wouldn't be surprised if the arm version just has them enabled.

@shiftleftplusone
Copy link

my comment first of all is about providing all float formatters for all printf-like functions or Mega and Due (e.g., sprintf, sscanf), and the rest is urgently needed for the future additionally !

Finally : float formatters don't work for sscanf on my Dues (neither 1.5.8 nor 1.6.0 ), and obviously not on my Megas!
And that's really a crap!

@PaulStoffregen
Copy link
Contributor

I just tried running this on my Arduino Due:

void setup() {
  Serial.begin(9600);
}

void loop() {
  char buf[256];
  float n = 123.456;
  sprintf(buf, "%.4f", n);
  Serial.print("buf: ");
  Serial.println(buf);
  int i;
  float r;
  i = sscanf(buf, "%f", &r);
  Serial.print("r: ");
  Serial.print(r);
  Serial.print(", i: ");
  Serial.println(i);
  delay(1000);
}

Here's what it prints in the serial monitor:

buf: 123.4560
r: 123.46, i: 1
buf: 123.4560
r: 123.46, i: 1
buf: 123.4560
r: 123.46, i: 1
buf: 123.4560
r: 123.46, i: 1
buf: 123.4560
r: 123.46, i: 1

@PaulStoffregen
Copy link
Contributor

Maybe you should give sprintf and sscanf another try?

@shiftleftplusone
Copy link

no, can't you read???

My functions look like this, and that's just what I expect them to work:

sprintf(sdata,"%08d %08d %08.3f %08.3f", p, q, x, y);
cnt = sscanf (sdata,"%08d %08d %08.3f %08.3f", &m, &n, &v, &w);

This is standard C syntax and I expect it to work like this and to read formatted floats and not just 0.00 for each float variable!!

And don't expect me to write code as much to sink a ship just for those effing "workarounds" !
Just enable a compiler pragma (or WTF ever) to fully support %f for sscanf !!

@PaulStoffregen
Copy link
Contributor

Post a complete program that can be copied into Arduino and run on a real board.

Your code fragment has 4 different variable names on each line.

@shiftleftplusone
Copy link

for all I care, ok, but that wouldn't change this issue:

// sprintf() plus sscanf()
// Sketch 1.6.0

void setup() {
  //  
  Serial.begin(9600);

  int16_t p=33, q=66, m, n, cnt;

  float x=PI, y=987.654, v, w;
  char sdata[64], sbuf[64];

  // pre-test
  Serial.println("Start pre-Test");
  Serial.println("Original x als float");
  Serial.println(x);
  Serial.println("Original-String: 1x float formatiert per sprintf()");
  sprintf(sdata,"%08.3f",x);
  Serial.println(sdata);

  cnt = sscanf (sdata,"%08.3f", &w);
  Serial.print("Anzahl Werte per sscanf()="); Serial.println(cnt);
  Serial.println("Kopie-Werte per sscanf()");
  Serial.println(w);
  Serial.println("Ende pre-Test");
  Serial.println();



  Serial.println("Original-Werte");
  Serial.println(p);
  Serial.println(q);
  Serial.println(x);
  Serial.println(y);

  Serial.println("Original-String per sprintf()");
  sprintf(sdata,"%08d %08d %08.3f %08.3f", p, q, x, y);
  Serial.println(sdata);  // Originalstring ausgeben


  cnt = sscanf (sdata,"%08d %08d %08.3f %08.3f", &m, &n, &v, &w);
  Serial.print("Anzahl Werte per sscanf()="); Serial.println(cnt);
  Serial.println("Kopie-Werte per sscanf()");
  Serial.println(m);
  Serial.println(n);
  Serial.println(v);
  Serial.println(w);



  strcpy(sbuf, sdata);
  Serial.println("Kopie-String");
  Serial.println(sbuf); // kopierten String ausgeben


  sscanf (sbuf,"%08d %08d %08.3f %08.3f", &m, &n, &v, &w);
  Serial.println("Kopie-Werte per sscanf()");
  Serial.println(m);
  Serial.println(n);
  Serial.println(v);
  Serial.println(w);


}

void loop() {
  // put your main code here, to run repeatedly:

}

@shiftleftplusone
Copy link

Serial output:

Start pre-Test
Original x als float
3.14
Original-String: 1x float formatiert per sprintf()
0003.142
Anzahl Werte per sscanf()=1
Kopie-Werte per sscanf()
0.00
Ende pre-Test

Original-Werte
33
66
3.14
987.65
Original-String per sprintf()
00000033 00000066 0003.142 0987.654
Anzahl Werte per sscanf()=3
Kopie-Werte per sscanf()
33
66
0.00
0.00
Kopie-String
00000033 00000066 0003.142 0987.654
Kopie-Werte per sscanf()
33
66
0.00
0.00

@PaulStoffregen
Copy link
Contributor

I just tried this on Linux:

#include <stdio.h>

int main()
{
        float f;

        sscanf("123.456", "%08.3f", &f); 
        printf("%f\n", f);
        return 0;
}

The compiler warns:

test.c: In function ‘main’:
test.c:7:2: warning: unknown conversion type character ‘.’ in format [-Wformat]
test.c:7:2: warning: too many arguments for format [-Wformat-extra-args]

When actually run, it prints:

0.000000

@PaulStoffregen
Copy link
Contributor

Maybe you should go post a string an angry comments on the gcc or linux dev lists?

@shiftleftplusone
Copy link

I can't see the point why it shouldn't work:

http://www.cplusplus.com/reference/cstdio/sscanf/

int sscanf ( const char * s, const char * format, ...);
Reads data from s and stores them according to parameter format into the locations given by the additional arguments, as if scanf was used, but reading from s instead of the standard input (stdin).
s
C string that the function processes as its source to retrieve the data.
format
C string that contains a format string that follows the same specifications as format in scanf (see scanf for details).

http://www.cplusplus.com/reference/cstdio/scanf/
f, e, g Floating point number A series of decimal digits, optionally containing a decimal point, optionally preceeded by a sign (+ or -) and optionally followed by the e or E character and a decimal integer (or some of the other sequences supported by strtod).
Implementations complying with C99 also support hexadecimal floating-point format when preceded by 0x or 0X.

@shiftleftplusone
Copy link

in an another post these following compiler options have been mentioned, maybe this is related to this issue - but I don't understand how to utilize it for Sketch though

printf:
-Wl,-u,vfprintf -lprintf_flt -lm

scanf:
-Wl,-u,vfscanf -lscanf_flt -lm

@PaulStoffregen
Copy link
Contributor

Arduino doesn't use the C99 standard.

But even when I compile that test with this "gcc -Wall -std=c99 test.c", the result is still:

test.c: In function ‘main’:
test.c:7:2: warning: unknown conversion type character ‘.’ in format [-Wformat]
test.c:7:2: warning: too many arguments for format [-Wformat-extra-args]

@shiftleftplusone
Copy link

well - then please tell me what's going on with this effing sscanf issue....?

@PaulStoffregen
Copy link
Contributor

Maybe gcc and the C library on Ubuntu 12.04 aren't following the standard either?

Or maybe you're mistaken about what syntax sscanf() is supposed to accept?

@cmaglie
Copy link
Member

cmaglie commented Feb 27, 2015

@vogonjeltz

@PaulStoffregen has already wasted a lot of his precious time to following this issue, I guess you should read the standard (that you love so much) more carefully:

http://www.cplusplus.com/reference/cstdio/scanf/

The dot "." format specifier is not allowed in sscanf, as the error message posted by Paul clearly explains.

@shiftleftplusone
Copy link

"optionally containing a decimal point"

@cmaglie
Copy link
Member

cmaglie commented Feb 27, 2015

Those are the "characters extracted" not the format specifier.

@PaulStoffregen
Copy link
Contributor

@cmaglie - I do have a little interest in this issue. For Teensy, I recently switched to different library settings, which exclude float support from these functions by default. There's a workaround, from sketch code, to get the linker to still use the float version.

@PaulStoffregen
Copy link
Contributor

Turns out, this conversation is unrelated... but this is something I'm wanting out for issues with, since there's a need to balance desires from users who want to target much smaller chips vs people who want float support (as I do occasionally).

@shiftleftplusone
Copy link

I'm not talking about teensy or nanos, I'm talking about Megas and Dues, and I don't see where's explicitely written that a dot is not allowed ?

@dgrat
Copy link
Author

dgrat commented Feb 27, 2015

Well, when I posted this thing I was not used to Arduino. I see the point to disable these features.
However, I wondered quiet a lot. Because it was not obvious.

@shiftleftplusone
Copy link

users for smaller chips may use a crippled io lib with mutilated features (or leave them completely off),
but users of large MCUs (e.g. ARM Cortex) must be able to #include std libs

@PaulStoffregen
Copy link
Contributor

As far as I'm concerned, whether or not a dot is allowed in a %f format specifier in sscanf() doesn't belong anywhere on Arduino's website, since Arduino doesn't officially support printf and scanf as part of their official API.

You can post another 1000 angry rants and none of it will convince anyone Arduino's sscanf() is broken if it gives the same result as sscanf() on Linux.

@shiftleftplusone
Copy link

ok, given a dot was not allowed (it finally is not written anywhere) - then I stand corrected, then that was simply missing in the documentation.

But to exclude float formatters by Sketch completely (as written in the post above) actually IS an Arduino Sketch issue.

@matthijskooijman
Copy link
Collaborator

But to exclude float formatters by Sketch completely (as written in the post above) actually IS an Arduino Sketch issue.

Yes, it is an issue, but as previously explained, this is an intentional trade-off between code size and features. If you really need it, modify platform.txt to add the proper linker options to enable it.

@shiftleftplusone
Copy link

users for smaller chips may use a crippled io lib with mutilated features (or leave them completely off),
but users of large MCUs (e.g. Mega or ARM Cortex) must be able to #include std libs

@shiftleftplusone
Copy link

I'll test the dot issue in the formatter ASAP when I'm at home with my Arduino Boards.

@PaulStoffregen
Copy link
Contributor

You're now ranting that large boards like Arduino Due should support sscanf, and indeed Arduino Due does.

@shiftleftplusone
Copy link

Till now it actually didn't work for the Due as I widely explained!
But that might have been related to the dot inside the formatstring which was not explicitely forbidden acc. to the cplusplus documentation.
And by Sketch, I also never got compiler warnings about '.' in formatstrings, too.
OTOH, Im not just talking just about Due but ALSO about the Mega. And for the Mega also float formatters didn't work even for sprintf so far !

But as I also have written that in case this was a deficiency in the documentation then I surely stand corrected!
And my very first objection was not about ranting the Due but contradicting the basic approach posted by some users like, e.g.
"...I believe that is by design because of the substantial extra space required for such features. ", and
"...as mentioned that needs link-time options, that will probably not be enabled by default due to the space overhead"

But no one ever had pointed out before so far (neither in this thread here nor somewhere else) that a dot inside a float formatter wouldn't be allowed at all!

@shiftleftplusone
Copy link

update:
ok - I tested it!
after replacing all %08.3f" by "%f" then sscanf (using Sketch 1.5.8) works fine with the DUE,
and having already installed the lib.c float patch it now also works with the Mega.

Thanks to all for your inputs !

@dsyleixa
Copy link

just for better search results:
a temporary workaround is provided by @oqibidipo , adding some patches to boards.txt:
https://github.com/arduino/Arduino/issues/5033#issuecomment-234335079

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Component: Core Related to the code for the standard Arduino API Type: Bug
Projects
None yet
Development

No branches or pull requests

10 participants