Hi Bruno!
I'm facing troubles with your change to gnulib regarding strtod. In particular
there is a problem with this code in lib/strtod.c:
/* The underlying implementation works fine, except for signed NaNs. */
DOUBLE
STRTOD (const char *nptr, char **endptr)
# undef STRTOD
# if defined USE_FLOAT
# undef strtof
# define STRTOD strtof
# define ISNAN isnanf
# elif defined USE_LONG_DOUBLE
# undef strtold
# define STRTOD strtold
# define ISNAN isnanl
# else
# undef strtod
# define STRTOD strtod
# define ISNAN isnand
# endif
{
DOUBLE value = STRTOD (nptr, endptr);
if (ISNAN (value))
{
while (locale_isspace (*nptr))
nptr++;
if (*nptr == '-' && !signbit (value))
value = - value;
}
return value;
}
This problem shows up when using musl built with tinycc. In this particular
case the NAN macro of musl is defined as (0.0f/0.0f) and at least on x86_64
this is -NaN and not +NaN.
If musl is compiled with gcc, then its NAN macro is defined as the
__builtin_nanf("") of gcc, which is +NaN.
The C standard does not specify the sign of the NAN macro, it is
implementation-defined¹.
Due to the NAN macro being -NaN and the fact that musl ignores the sign of a NaN², which is
generally fine and not violating any standard, the results of strtod("nan") and
strtod("-nan") are both that NAN macro and thus -NaN.
I'm pretty sure, that this handling of musl is correct standard wise, although
certainly surprising.
This general -NaN result of strtod is not expected by gnulib and not handled
properly and therefore test-strtod and test-c-strtod fail.
I see two issues with the code above. First of all, it only tries to set the
sign bit if the string has the minus sign. It does not check if the result is
-NaN in case the string is missing the minus sign.
The second problem is with "value = - value;". This is not guaranteed to change
the sign of any ±NaN value. The C standard seems to only offer two functions to work with
the sign of NaN values portably³⁴, which are signbit and copysign.
I kindly propose a fix to use copysign to set the expected sign either
unconditionally or if detecting a sign mismatch.
if ((*nptr == '-') == !signbin (value))
value = copybit(value, *nptr == '-' ? -1 : +1);
Bye
Stefan
¹ "There are many different NaN values, differentiated by their payloads and their
sign bits. The contents of the payload and the sign bit of the NaN generated by the macro
NAN are implementation-defined."
<https://cppreference.com/c/numeric/math/NAN>
² "Quiet nans are treated equally (there is only one logical nan value, it is
printed as “nan” or “-nan”, the library otherwise does not care about the sign and
payload of nan)"
<https://wiki.musl-libc.org/mathematical-library>
³ "This macro detects the sign bit of zeroes, infinities, and NaNs. Along with
copysign, this macro is one of the only two portable ways to examine the sign of a
NaN."
<https://cppreference.com/c/numeric/math/signbit>
⁴ "copysign is the only portable way to manipulate the sign of a NaN value (to
examine the sign of a NaN, signbit may also be used)."
<https://cppreference.com/c/numeric/math/copysign>