svn commit: r253215 - head/lib/msun/src

Bruce Evans brde at optusnet.com.au
Mon Jul 29 13:30:20 UTC 2013


On Mon, 29 Jul 2013, David Schultz wrote:

> On Thu, Jul 11, 2013, David Chisnall wrote:
>> +static __inline int
>> +__inline_isnan(double __x)
>> +{
>> +
>> +	return (__x != __x);
>> +}
>> +
>> +static __inline int
>> +__inline_isnanf(float __x)
>> +{
>> +
>> +	return (__x != __x);
>> +}
>> +
>> +static __inline int
>> +__inline_isnanl(long double __x)
>> +{
>> +
>> +	return (__x != __x);
>> +}
>
> This has already been covered at greater length, but I believe
> this part is incorrect.

The correctness of this part has always been covered at greater
length :-).  We use simple "!=" tests because there is no way to
get correct behaviour and the simple test is more correct than
most, including the extern function implementation, and faster
than all.  Even greater length follows :-).

> Relational operators can raise an invalid
> exception when one of the arguments is a NaN -- even a quiet NaN.
> Raising an exception is optional in C99 (7.12.14) and required in
> IEEE-754... in practice, it tends to be platform- and compiler-
> specific.

IEEE-754-2008 actually requires the '=' and '!=' comparisions (with
that spelling (or with the alternative spellins '?<>', or 'NOT=' for
'!=')) to _not_ generate an exception for quiet NaNs.  It has an
unordered-signaling and and unordered-quiet variant for all
comparisions.  The equality and inequality signaling comparisions are
so unusual that IEEE-754-2008 doesn't even give normal spellings for
their abbreviations in its tables. The tables in IEEE-854-1987 seem
to be better, and give these as '<>' and 'NOT<>'.  Howver, for
inequality predicates like '<', it is the unordered-signaling variants
that have the normal spelling, with unordered-quiet '<' having the
special spelling '?<'.

I first thought that the 2 types of comparisons were new in 2008, but
now see that they were in the IEEE-854-1987 with only a small difference
in emphasis.

The unordered-quiet variants are intended to be used in code that
takes into account the possibility of NaNs.  That is almost no code.
I had misremembered this (signaling for the usual case) as being a
design error, because real hardware like i387 and SSE has limited
support for signaling.  Actually, i387 and SSE have full support
for both cases (FCOM and COM give the signaling behaviour, and FUCOM
and UCOM give the quiet behaviour).  The only documentation that I
can find now that says that FCOM should never be used is where I
probably learned it from in _The 8086/386 Architecture_ by Morse
et al.  This says:
     Since the 8087 was introduced, the floating-point standard
     specified that comparisons involving quiet NaNs should return a
     result of \"unordered,\" with no exception raised.  Since the FUCOM
     instruction raises the invalid exception, in this case it fails
     to meet the standard.  So the _unordered comparison_ instructuctions
     FUCOM, FUCOMP, and FUCOMPP were added to the 387.  [...]
     [Recommendation to use FCOM in simple programs and let the exception
     handler deal with NaN results.  Use FUCOM if we want to handle NaNs
     and the unordered condtion ourselves.]
But the standard actually requires both, at least in 1987 when Morse's
book was published.

C99 requires the signaling variants, except possiblly for the equality
predicates.  For inequality, islessgreater() gives IEEE quiet inequality
'?<>' or !'=' except it is underspecified for NaNs.  You can't get
IEEE quiet equality by inverting this since it must not be inverted for
NaNs.  You can build quiet IEEE equality and using this and
isunordered(), but the logic for this is complicated and it would be
hard for compilers to turn it back into direct IEEE quiet unordered 
equality comparisons.

C99 clarifies the intended behaviour in footnote 194, but footnotes
are not part of the standard:

%%%
        194IEC 60559 requires that the built-in relational operators
           raise the  invalid  exception  if  the  operands  compare
           unordered,  as  an  error  indicator for programs written
           without consideration of NaNs; the result in these  cases
           is false.
%%%

So '=' and '!=' should always give 'false' for NaNs, and the macros
should have the same behaviour.

x86 C compilers understand none of this.  They seem to have started
avoiding using FCOM since before Morse's book came out, and now always
generate FUCOM/UCOM.  So they always give unordered comparisons.  So
the support for "simple" programs is broken by default, and there is
no way to get it (no flag for stricter IEEE conformance?), and the C
comparison macros are useless in unportable x86 code since they generate
the same unordered comparison instructions as direct comparisons.
More usefully, the implemenation can be unportable, so it can depend
on these bugs in <math.h>.  Using the x != x comparision without an
ifdef is perhaps too x86-specific, but the other arches are likely to
be even more broken no one cares about them.  I just tested the following
program on amd64, ia64 and sparc64.  It fails to raise invalid for the
comparision of NaNs on all of them:

 	#include <fenv.h>
 	#include <math.h>
 	#include <stdio.h>

 	double z;

 	int
 	main(void)
 	{
 		z = z / z;
 		printf("%d\n", fetestexcept(FE_INVALID));
 		feclearexcept(FE_INVALID);
 		if (z >= z)
 			printf("equal\n");
 		printf("%d\n", fetestexcept(FE_INVALID));
 		return (0);
 	}

> That is the whole reason the is* macros are defined by the
> standard in the first place, and also why we didn't use the
> trivial implementation above.  The is* macros are required to not
> raise an exception.
>
> P.S. It would be great if clang implemented the FENV_ACCESS pragma
> and provided an intrinsic that produced a fast inline isnan() when
> the pragma is off, and the full, correct one when the pragma is on.

Not really great, since the ifdefs to actually use this would be
messy.  It would take an especially unportable pragma to switch
between quiet (technically broken) and signaling comparisons, and
and this pragma would be especially useless on x86 since both
types of comparisons are supported directly in hardware so there
are no efficiency advantages from using the technically broken one.

Anwyay, FENV_ACCESS is irrelevant, since the fast inline isnan() is
independent of it.  The fast inline isnan() already exists as
__builtin_isnan() for clang and as plain isnan() or __builtin_isnan()
for gcc-4+.  But ifdefs for this would be too messy.

Exceptions for comparing NaNs are more interesting if trapping of the
invalid exception is enabled.  Otherwise, they no difference except
for non-simple programs that are aware enough of NaNs to test for the
invalid exception at least once.  Even non-simple programs shouldn't
be handling NaNs so tenderly as to avoid subsequent exceptions for
them, as they were apparently expected to do in Morse's book.  I
think exceptions for comparison are actually most useful for the
non-simple programs.  Simple programs would raise invalid when they
generate a NaN and never re-raise it, and only notice if it is trapped.
Non-simple programs might clear the invalid exception occasionally
and prefer it to be raised by comparisons later, instead of ensuring
that NaNs don't propagate.  And if they look at their data too much
so as to weed out NaNs, then exceptions for comparisons are useful
for detecting cases that the weeding missed.

Bruce


More information about the svn-src-head mailing list