[PATCH RESEND 01/14] stdio-common: Add tests for formatted scanf input specifiers
Maciej W. Rozycki
macro@redhat.com
Thu Feb 20 14:19:10 GMT 2025
On Sat, 15 Feb 2025, Joseph Myers wrote:
> > + We need to be careful with parsing reference data in the handling of
> > + the sign as with the IBM long double binary format unary negation
> > + of a positive value whose lower-magnitude part is zero produces a
> > + bit pattern that is different from one produced by initialization or
> > + conversions made by functions such as 'scanf' or 'strtod' from the
> > + complementing negative value. The difference is in the sign of zero
> > + held in the lower-magnitude part and therefore the bit patterns are
> > + arithmetically equivalent, but they do not match byte-wise.
> > +
> > + Therefore we set the sign with the initial value of the significand,
> > + either -0.0 or 0.0, which sets the sign of both parts according to
> > + what 'scanf' does, and never negate it afterwards.
> > +
> > + Additionally the Intel long double binary format uses only 80 bits
> > + out of 96 that the data type occupies. Therefore preinitialize the
> > + reference value with the same 0xa5 bit pattern that the value under
> > + test has been so that the byte-wise comparison matches as well for
> > + the unused parts of the two data pieces.
>
> I don't think it's safe to make any assumptions about whether padding bits
> of x86 (or m68k?) long double are or are not modified by scanf; that may
> depend on the details of what instructions the compiler happens to
> generate in scanf to store the long double value that's been read. So I
> don't think the strategy of comparing with memcmp is safe in that case,
> unless you first copy just the value bits (and not padding bits) to a
> character array (or other type without room for the padding bits) before
> comparing.
Speaking of x86 only I fail to see why the compiler should access the
unused 16-bits beyond the underlying hardware data type. None of the FPU
instructions does, based on my past experience and I have double-checked
with Intel SDM too. NB this format is x87-only and therefore very old,
going back to 1980, so I expect hardware implementations to be very
conservative lest they break software compatibility.
So any access there would be by an explicit store of some data only to
the sign/exponent component, presumably by an integer instruction, though
32-bit FIST/FISTP could be thought of too even if barely. But then why do
a 32-bit store when 16-bit ones are available and can do exactly the same
without clobbering bits outside actual data? Then it's not the kind of
operation I'd expect a compiler to produce for an FP operation, and if it
were produced from our code, then obviously it would be completely under
our control.
This testset indicates no such store happens and while I agree the
contents of the padding bits are unspecified, I think it is going to be
worthwhile to keep it verified that the current situation does not change
without raising awareness. With no such test it may happen inadvertently.
With it in place we can decide at the time whether to back out the change
or to update the test accordingly.
I can't speak of m68k offhand, but given that likewise it comes from the
time when memory was expensive I'd expect m68k hardware not to poke at
bits beyond the FP datum itself. I could double-check I suppose, though I
don't have m68k hardware to hand, so that would be by documentation only.
The verifier handler can be easily adjusted if we decide it's indeed
needed with a sysdep-specific variant of tst-scanf-format-real.h. I'm not
convinced it is at this point though.
> For IBM long double, the cases where the bit pattern is not fully defined
> are (a) the low 64 bits of a NaN are entirely unspecified; (b) if the low
> part is zero, its sign does not matter (regardless of the value of the
> high part). You're less likely to have things varying with the details of
> compiler code generation there, however.
My understanding has been (and please correct me if I'm wrong) that the
IBM long double format is software-assisted only; I have double-checked
with documentation I have to hand and at least "Power ISA, Version 3.1"
dated May 1, 2020 only mentions 32-bit single and 64-bit double binary
hardware FP formats.
So actually we have less control here with actual FP operations made by
libgcc or whatever equivalent other compilers may use. As I noted for the
time being I have carefully avoided the ambiguities in the results at
least for the libgcc implementation, and maybe it would be worth it if the
implementation was updated such as to make the sign of low-part zero agree
with the sign of the high part.
Again a sysdep-specific variant of tst-scanf-format-real.h could be
provided instead, even if checking for the special cases of NaN data and
zero low-part, but I'm not convinced at this point.
> For NaNs in general, if a payload is *not* specified in the scanf string
> then you'll get whatever default NaN scanf returns, while computing some
> default NaN in the test code. Maybe these are in fact reliably the same,
> but it's beyond the API I'd consider to be guaranteed.
With ISO C being deliberately vague here I think implementation-defined
does not preclude from being consistent, under the principle of least
surprise. I'd expect the canonical NaN encoding to be always produced
where a specific payload has not been requested (which we ignore anyway),
and we can certainly verify for output to stay consistent.
> In short: for x86/m68k long double I think the comparison approach used is
> unreliable, depending on the compiler version, because it relies on
> padding bits not changing. For IBM long double and for NaNs without
> payload specified, I think comparing using memcmp means you're at least
> testing internal implementation details (whether intentionally or
> otherwise) that go beyond what I think is properly part of the specified
> API for glibc.
I think there is value in verifying the consistency of the implementation
even if it goes beyond the specified API.
NB I note that tst-scanf-format-c-c.input data marked BZ12701 cannot be
made to work with this testset for the lack of a way (except for `sscanf',
`vsscanf') in the current implementation to force EOF at the separator
colon. An independent testset will be needed, but I'm going to keep these
markers for the time being for documentation purposes, until the part of
BZ #12701 concerned has been resolved.
Maciej
More information about the Libc-alpha
mailing list