NetBSD-Bugs archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
lib/60015: x87 makes testing signalling NaN difficult
>Number: 60015
>Category: lib
>Synopsis: x87 makes testing signalling NaN difficult
>Confidential: no
>Severity: serious
>Priority: medium
>Responsible: lib-bug-people
>State: open
>Class: sw-bug
>Submitter-Id: net
>Arrival-Date: Tue Feb 17 23:25:00 +0000 2026
>Originator: Taylor R Campbell
>Release: current
>Organization:
The NanBSD/i387 Roundation, Inc.
>Environment:
>Description:
The C23 fpclassify() function and its cousins isnan(),
isinf(), isnormal(), &c., are required, in implementations
supporting Annex F (IEC 60559 floating-point arithmetic, more
popularly known as IEEE 754), _not_ to raise floating-point
exceptions _even if_ the inputs are signalling NaN:
> 6. The C classification macros fpclassify, iscanonical,
> isfinite, isinf, isnan, isnormal, issignaling, issubnormal,
> iszero, and signbit provide the IEC 60559 operations
> indicated in the table above provided their arguments are in
> the format of their semantic type. Then these macros raise no
> floating-point exceptions, even if an argument is a signaling
> NaN. --C23, Annex F, Sec. F.3
(This language appears to be new in C23; I can't find it in C99
or C11, though I haven't tracked down the specific change that
added it in C23.)
It is tempting to try something like this, as in t_fpclassify.c
rev. 1.21 for double (simplified for the double-only case):
bool
issignalling(double f)
{
union { double f; uint64_t i; } u = { .f = f };
return isnan(f) && (u.i & (DBL_SNANBIT|DBL_QNANBIT)) == DBL_SNANBIT;
}
Unfortunately, actually using this can prove difficult. The
following assertion should be guaranteed not to fire:
feclearexcept(FE_ALL_EXCEPT);
x = issignalling(nan);
assert(fetestexcept(FE_ALL_EXCEPT) == 0);
But on x87, mere floating-point load instruction (FLDL) to load
a double (IEEE 754 binary64) into the floating-point register
stack converts it to long double format (x87 80-bit extended
precision) which maps signalling NaN to quite NaN and, as a
side effect, raises FE_INVALID.
So issignalling might wrongly return false _and_ calling it
might raise a floating-point exception.
>How-To-Repeat:
Expected output with the following program:
issignalling=1 except=0x0
Actual output on i386:
issignalling=0 except=0x1
#include <fenv.h>
#include <float.h>
#include <math.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#if defined __hppa__ || defined __mips__ /* just, why? */
# define QNANBIT 0
# define SNANBIT 1
#else
# define QNANBIT 1
# define SNANBIT 0
#endif
#define DBL_QNANBIT (QNANBIT*((uint64_t)1 << (DBL_MANT_DIG - 2)))
#define DBL_SNANBIT (SNANBIT*((uint64_t)1 << (DBL_MANT_DIG - 2)))
bool
issignalling(double f)
{
union { double f; uint64_t i; } u = { .f = f };
return isnan(f) && (u.i & (DBL_SNANBIT|DBL_QNANBIT)) == DBL_SNANBIT;
}
volatile union {
double d;
uint64_t u64;
} u = { .u64 = 0x7ff0000000000001 };
int
main(void)
{
bool result;
int except;
feclearexcept(FE_ALL_EXCEPT);
result = issignalling(u.d);
except = fetestexcept(FE_ALL_EXCEPT);
printf("issignalling=%d except=0x%x\n", result, except);
fflush(stdout);
return ferror(stdout);
}
Relevant excerpt of generated code -- note that the value of
u.d is loaded with FLDL only to store it on the stack again
with FSTPL for passing to __isnand, which both raises an
exception (FE_INVALID) and changes the input to __isnand so
that it is a quiet NaN:
16: dd 05 00 00 00 00 fldl 0x0
18: R_386_32 u
1c: dd 5c 24 18 fstpl 0x18(%esp)
20: f2 0f 10 44 24 18 movsd 0x18(%esp),%xmm0
26: f2 0f 11 04 24 movsd %xmm0,(%esp)
2b: e8 fc ff ff ff call 2c <main+0x2c>
2c: R_386_PC32 __isnand
30: 85 c0 test %eax,%eax
32: 74 50 je 84 <main+0x84>
34: 8b 54 24 1c mov 0x1c(%esp),%edx
38: 89 d0 mov %edx,%eax
3a: c1 e8 13 shr $0x13,%eax
3d: 83 f0 01 xor $0x1,%eax
40: 89 c3 mov %eax,%ebx
42: 83 e3 01 and $0x1,%ebx
>Fix:
Find a flux capacitor and a place to drive 88mph during a
lightning storm so we can have a word with engineers at Intel
in 1985.
Home |
Main Index |
Thread Index |
Old Index