[PATCH] stdio: make scanf(%f) eat the same number of characters as strtod() would

Miklós Máté mtmkls@gmail.com
Sat Jan 4 00:27:38 GMT 2025


On 02/01/2025 18:08, Joseph Myers wrote:
> On Wed, 1 Jan 2025, Miklós Máté wrote:
>
>> According to the spec the %f conversion specifier (and its synonyms) must
>> behave exactly as strtod(). This behavior matches FreeBSD libc. On MSVC
> No, scanf should read a string that is *or is a prefix of* a matching
> string.  (Footnote 330 in C23 says "fscanf pushes back at most one input
> character onto the input stream. Therefore, some sequences that are
> acceptable to strtod, strtol, etc., are unacceptable to fscanf.".)  If
> it's a prefix but not a matching sequence, that is a matching failure.
> See bug 12701 and duplicates (18246 is probably a duplicate though not
> currently marked as such).
>
> If you're interested in working in this area, talk to Maciej first since
> he's working on scanf testing.
>
Thanks for the clarifications. I re-read the standard, and the listed 
bug reports, and I think now I understand how the spec wants %f to work. 
In Example 3 (this example is also part of tstscanf.c) the "100ergs" 
string is invalid, because fscanf reads until 'r', which can't be part 
of a float, pushes it back, and tries to convert "100e". The result of 
the conversion is d=100.0, and 3 characters are to be consumed, but 
fscanf can't push back 'e', because that would be the second pushback. 
Therefore, it's considered a matching failure, and the result of fscanf 
is -1 with no variable assignments.

This is the behavior of MSVC.

FreeBSD ignores the 1 character pushback limit. It accepts d=100.0 for 
%f, consumes 3 characters (pushes back 2), and after that %s gets 
"ergs". I think this is the most user-friendly approach, if multiple 
pushback is supported. Their man page for ungetc says "One character of 
push-back is guaranteed, but as long as there is sufficient memory, an 
effectively infinite amount of pushback is allowed.", and they are using 
this ability in fscanf.

In my opinion glibc gives the worst result. It accepts d=100.0 for %f, 
consumes 4 characters, and %s gets "rgs" with no error. This is neither 
user-friendly, nor spec conformant.

Given that the libio framework already supports multiple pushback, I 
think the best way would be to do what FreeBSD does (i.e. I still stand 
by my patch). The 1 character limit for ungetc in the standard is 
probably there to support memory-constrained platforms, I think it's 
safe to ignore it on PC.



More information about the Libc-alpha mailing list