Does posix say anything about the sign in NaNs ?

Bruce Evans brde at optusnet.com.au
Thu Dec 25 14:01:00 UTC 2014


On Wed, 24 Dec 2014, Pedro Giffuni wrote:

> I got the attached patch from OpenBSD.
>
> It says:
> ____
> Show the sign for NaN as per POSIX; from Elliott Hughes.
> ok martynas@, millert@, doug@
> ____
>
> I can't find a reference in POSIX documentation to support it though.

The behaviour is implementation-defined.  From n869.txt for printf:

X                A  double  argument  representing  an  infinity   is
X                converted in one of the styles [-]inf or [-]infinity
X                 --   which  style  is  implementation-defined.    A
X                double  argument  representing a NaN is converted in
X                one of the styles [-]nan or [-]nan(n-char-sequence)
X                 --  which style, and the  meaning  of  any  n-char-
X                sequence,    is   implementation-defined.    The   F
X                conversion specifier produces INF, INFINITY, or  NAN
X                instead of inf, infinity, or nan, respectively.220)

"style" is not clearly defined.  The format for input in strtod()
is formally defined as [-]something without using the term "style",
but the specication for actually handling the minus sign is even less
complete (see below).

The library intentionally suppresses the sign for NaNs on output.
This is consistent with it ignororing the sign for NaNs on input.


> Anyone has a reason why we shouldn't adopt it, or a reference I can quote
> on the commit log?

It is not needed, and is inconsistent with the treatment for input.
It is not very useful.  Consistent, documented support for the
[-]nan(n-char-sequence) support would be useful.  This support should
be like what gdb does, but better.

Leave it for the gdtoa vendor to change.

For input, then .  The details are unspecified (not even
implementation-defined) and very machine-dependent in practice even for
the negating normal numbers.  From n869.txt for strtod:

X        string.   If  the subject sequence begins with a minus sign, |
X        the  sequence  is  interpreted  as negated.235)  A character

footnote 235:
X           It is unspecified  whether  a  minus-signed  sequence  is
X           converted  to  a  negative number directly or by negating
X           the value resulting  from  converting  the  corresponding
X           unsigned  sequence  (see  F.5); the two methods may yield
X           different results  if  rounding  is  toward  positive  or
X           negative  infinity.   In either case, the functions honor
X           the sign of zero if  floating-point  arithmetic  supports
X           signed zeros.

This is under-specified.  "interpreted as negated" is not defined
anywere, so it must have its English meaning, and that meaning is
unclear in all cases where rounding or NaNs are involved, and also for
the arcane -0.  The footnote doesn't specify anything, since footnotes
are not part of the standard.  It just gives the hint that any
reasonable method may be used, although the methods give different
results.  It gives the hint that -0 should work.  It doesn't say
anything for NaNs.  The two methods may give different results for
NaNs too.  Direct interpretation sets the minus bit, as if by copysign()
from -1.  Whether negating a NaN toggles its sign bit is very
implementation-dependent.

   (The behaviour differs even on i386.  Negation is normally
   implemented using fchs in i387.  SSE doesn't have fchs, so
   negation must be implemented as subtraction from 0, and IIRC
   this doesn't change the sign bit for NaNs.  This gives the
   follow behaviours on i386 for -x on the NaN _variable_ x:
   - gcc for all precisions: all cases use fchs, so flip the sign
   - clang -msse*: clang is incompatible.  It uses SSE* for float
     and double precision, but fchs for long double precision.
     Thus -x flips the sign bit for long double precision only.
   And on amd64:
   - both gcc and clang use SSE for float and double precision, so
     the behaviour is the same as for clang -msse* on i386.

   Toggling the sign bit for +-0 has the same machine-dependenices
   as for NaNs if done naively.  The library cares about this case.
   I think it uses a direct method (more hackish than copysign)
   since anything involving an expression would be even more
   unportable than hacking on the sign bit.)

I'm not sure how the library handles "-nan" on input, but since it
produces a NaN with a sign bit of 0 on i386, it apparently forces
the sign bit to 0, unlike what it does for "-0" and "-0.1")
(replace 0.1 by a number that should be affected by the rounding
mode if necessary to get an interesting example).

In my version of libm, most of the 2-arg functions have hackish changes
related to the above machine dependencies to force consistent results
for pairs of NaNs.  To stop the result of x+y (where x and y are NaNs)
depending on the quiet bit and the precision (due to it being evaluated
in different register sets for different precisions) and sometimes
also on the compiler ordering of x+y, expressions are forced to long
double precision early.  Hardware generally chooses one of x or y
(quieted) for the result of x+y.  If the result depends on the ordering
and the ordering is unpredictable, then sign bits in NaNs get lost
just like value bits in NaNs.


Bruce


More information about the freebsd-standards mailing list