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>


Reply via email to