Bug 57484

Summary: 'std::numeric_limits< T >::signaling_NaN()' signaling-bit is incorrect for x86 32-bit.
Product: gcc Reporter: Charles L. Wilcox <zxClhzAApX1EdJwQANqrjLERmFeURQVy>
Component: targetAssignee: Not yet assigned to anyone <unassigned>
Status: RESOLVED WONTFIX    
Severity: normal CC: daniel.kruegler, rguenth, tschwinge, ubizjak
Priority: P3    
Version: 4.8.1   
Target Milestone: ---   
See Also: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=71460
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=58416
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56831
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=114659
Host: Target: i686-pc-linux-gnu
Build: Known to work:
Known to fail: Last reconfirmed:
Attachments: C++ test program to extract and display the signaling-bit of a signaling-NaN and a quiet-NaN.
Updated test code to print out the full signaling and quiet NaN's values in hex.
Expanded unit-test code to set signaling-NaN values via 'union' aliasing.

Description Charles L. Wilcox 2013-05-31 15:24:53 UTC
Created attachment 30234 [details]
C++ test program to extract and display the signaling-bit of a signaling-NaN and a quiet-NaN.

$ g++ -v
Using built-in specs.
COLLECT_GCC=g++
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/4.7/lto-wrapper
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Ubuntu/Linaro 4.7.3-1ubuntu1' --with-bugurl=file:///usr/share/doc/gcc-4.7/README.Bugs --enable-languages=c,c++,go,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-4.7 --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --with-gxx-include-dir=/usr/include/c++/4.7 --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --enable-gnu-unique-object --enable-plugin --with-system-zlib --enable-objc-gc --with-cloog --enable-cloog-backend=ppl --disable-cloog-version-check --disable-ppl-version-check --enable-multiarch --disable-werror --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --with-tune=generic --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
Thread model: posix
gcc version 4.7.3 (Ubuntu/Linaro 4.7.3-1ubuntu1) 


What I expect:
For 'float' and 'double' types when 'std::numeric_traits< T >::is_iec559' is 'true', 'std::numeric_traits< T >::signaling_NaN()' should generate a IEEE-754 value whose sign bit is '0', exponent bits are all '1', the highest significand bit is '0', and some other significant bit(s) are '1'.

What I observe:
 - x86 32-bit:  all expectations are met, except that the highest significand bit is '1'.  ( This implies it is a quiet-NaN, not a signaling-NaN. )
 - x86 64-bit:  all expectations are met.

My evidence:
Attached is a test C++ program that extracts the IEEE-754 signaling bit value of both a signaling-NaN and a quiet-NaN, for 'float' and 'double' types, and prints the results to console.  When compiling with '-m32', I see the unexpected behavior; when compiling with '-m64', I see the expected behavior.

Compiler flags / command-line:
 - x86 32-bit:  g++ -m32 -Wall -Wextra test-signaling-nan.cpp
 - x86 64-bit:  g++ -m64 -Wall -Wextra test-signaling-nan.cpp

Compilation errors / warnings:
None.
Comment 1 Daniel Krügler 2013-05-31 18:50:22 UTC
I haven't checked your bit arithmetics, but I have checked the full bit patterns of the resulting NaNs in hex on my mingw-64 bit system. What I'm getting for are the following results:

1) gcc 4.7.2/4.9.0 20130519 (experimental) -m32:

Signaling NaN's of "f" is 7fa00000
Quiet     NaN's of "f" is 7fc00000
Signaling NaN's of "d" is 7ff4000000000000
Quiet     NaN's of "d" is 7ff8000000000000

2) gcc 4.7.2/4.9.0 20130519 (experimental) -m64:

Signaling NaN's of "f" is 7fa00000
Quiet     NaN's of "f" is 7fc00000
Signaling NaN's of "d" is 7ff4000000000000
Quiet     NaN's of "d" is 7ff8000000000000

All values look consistent with valid sNaN/qNaN patterns according to the ranges listed here:

http://www.doc.ic.ac.uk/~eedwards/compsys/float/nan.html

What are the full bit patterns that you get by printing the signaling_NaN.bits/quiet_NaN.bits values in hex?
Comment 2 Charles L. Wilcox 2013-06-01 14:26:52 UTC
Created attachment 30239 [details]
Updated test code to print out the full signaling and quiet NaN's values in hex.
Comment 3 Charles L. Wilcox 2013-06-01 14:31:28 UTC
Daniel,

Unfortunately, my initial machine, a laptop, decided to commit seppuku yesterday.

Fortunately, the disk survived, I ported the code to another machine I have, and am still seeing similar results there.

$ gcc -v
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/4.7/lto-wrapper
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Debian 4.7.2-5' --with-bugurl=file:///usr/share/doc/gcc-4.7/README.Bugs --enable-languages=c,c++,go,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-4.7 --enable-shared --enable-linker-build-id --with-system-zlib --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --with-gxx-include-dir=/usr/include/c++/4.7 --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --enable-gnu-unique-object --enable-plugin --enable-objc-gc --with-arch-32=i586 --with-tune=generic --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
Thread model: posix
gcc version 4.7.2 (Debian 4.7.2-5) 


I updated my code to print out the full contents of the NaN values in hex.

My machine produces the following output:

$ g++ -Wall -Wextra test-signaling-nan.cpp -m32
$ ./a.out 
Signaling NaN for type "f" in hex is "7fe00000".
Signaling NaN's signaling-bit status for type "f" is set to "true".
Quiet NaN for type "f" in hex is "7fc00000".
Quiet NaN's signaling-bit status for type "f" is set to "true".
Signaling NaN for type "d" in hex is "7ffc000000000000".
Signaling NaN's signaling-bit status for type "d" is set to "true".
Quiet NaN for type "d" in hex is "7ff8000000000000".
Quiet NaN's signaling-bit status for type "d" is set to "true".

$ g++ -Wall -Wextra test-signaling-nan.cpp -m64
$ ./a.out 
Signaling NaN for type "f" in hex is "7fa00000".
Signaling NaN's signaling-bit status for type "f" is set to "false".
Quiet NaN for type "f" in hex is "7fc00000".
Quiet NaN's signaling-bit status for type "f" is set to "true".
Signaling NaN for type "d" in hex is "7ff4000000000000".
Signaling NaN's signaling-bit status for type "d" is set to "false".
Quiet NaN for type "d" in hex is "7ff8000000000000".
Quiet NaN's signaling-bit status for type "d" is set to "true".

The resulting values on 32-bit vs. 64-bit are different, and I believe the 64-bit values are the correct ones.
Comment 4 Daniel Krügler 2013-06-01 15:32:09 UTC
(In reply to Charles L. Wilcox from comment #3)
> Signaling NaN for type "f" in hex is "7fe00000".

I agree, this one doesn't look right to me, because that looks indeed like a valid qNaN bit pattern only.

> Quiet NaN for type "f" in hex is "7fc00000".

OK.

> Signaling NaN for type "d" in hex is "7ffc000000000000".

Similar problem here: Only a qNaN

> Quiet NaN for type "d" in hex is "7ff8000000000000".

OK.

> $ g++ -Wall -Wextra test-signaling-nan.cpp -m64

> Signaling NaN for type "f" in hex is "7fa00000".
> Quiet NaN for type "f" in hex is "7fc00000".
> Signaling NaN for type "d" in hex is "7ff4000000000000".
> Quiet NaN for type "d" in hex is "7ff8000000000000".

All OK.

> The resulting values on 32-bit vs. 64-bit are different, and I believe the
> 64-bit values are the correct ones.

I agree.
Comment 5 Paolo Carlini 2013-06-03 09:25:22 UTC
Just to clarify that this is neither a library neither a C++ front-end issue: <limits> just uses the various __builtin_nans*
Comment 6 Charles L. Wilcox 2013-06-03 14:17:12 UTC
(In reply to Paolo Carlini from comment #5)
> Just to clarify that this is neither a library neither a C++ front-end
> issue: <limits> just uses the various __builtin_nans*

Paolo,

I guessed this was the case from what I could tell using the debugger, but I wanted some confirmation that I was correct.  Should the "Component" be changed from C++ to something else?
Comment 7 Paolo Carlini 2013-06-03 15:26:05 UTC
middle-end seems more appropriate. Richard, can you help me triaging this? Thanks in advance.
Comment 8 Charles L. Wilcox 2013-06-04 13:02:13 UTC
FWIW, I tried this with g++ 4.8 on a 32-bit only system I have; it still produces erroneous sNaN values there:

$ g++-4.8 -v
Using built-in specs.
COLLECT_GCC=g++-4.8
COLLECT_LTO_WRAPPER=/usr/lib/gcc/i486-linux-gnu/4.8/lto-wrapper
Target: i486-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Debian 4.8.1-1' --with-bugurl=file:///usr/share/doc/gcc-4.8/README.Bugs --enable-languages=c,c++,java,go,d,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-4.8 --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --with-gxx-include-dir=/usr/include/c++/4.8 --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --enable-gnu-unique-object --enable-plugin --with-system-zlib --disable-browser-plugin --enable-java-awt=gtk --enable-gtk-cairo --with-java-home=/usr/lib/jvm/java-1.5.0-gcj-4.8-i386/jre --enable-java-home --with-jvm-root-dir=/usr/lib/jvm/java-1.5.0-gcj-4.8-i386 --with-jvm-jar-dir=/usr/lib/jvm-exports/java-1.5.0-gcj-4.8-i386 --with-arch-directory=i386 --with-ecj-jar=/usr/share/java/eclipse-ecj.jar --enable-objc-gc --enable-targets=all --enable-multiarch --with-arch-32=i586 --with-multilib-list=m32,m64,mx32 --with-tune=generic --enable-checking=release --build=i486-linux-gnu --host=i486-linux-gnu --target=i486-linux-gnu
Thread model: posix
gcc version 4.8.1 (Debian 4.8.1-1) 
$ g++-4.8 -Wall -Wextra test-signaling-nan.cpp 
$ ./a.out 
Signaling NaN for type "f" in hex is "7fe00000".
Signaling NaN's signaling-bit status for type "f" is set to "true".
Quiet NaN for type "f" in hex is "7fc00000".
Quiet NaN's signaling-bit status for type "f" is set to "true".
Signaling NaN for type "d" in hex is "7ffc000000000000".
Signaling NaN's signaling-bit status for type "d" is set to "true".
Quiet NaN for type "d" in hex is "7ff8000000000000".
Quiet NaN's signaling-bit status for type "d" is set to "true".
Comment 9 Charles L. Wilcox 2013-07-09 15:54:12 UTC
So, given a month has gone by, should I expect any response or action on this bug-report?
Comment 10 Paolo Carlini 2013-07-09 16:53:37 UTC
Maybe Uros can help.
Comment 11 Uroš Bizjak 2013-07-09 18:36:23 UTC
(In reply to Paolo Carlini from comment #10)
> Maybe Uros can help.

On an x86 target using the legacy x87 instructions and the 80-bit registers, a load of a 64-bit or 32-bit value in memory into the 80-bit registers counts as a format conversion and an signaling NaN input will turn into a quiet NaN in the register format.
Comment 12 Uroš Bizjak 2013-07-09 18:54:30 UTC
(In reply to Uroš Bizjak from comment #11)
> (In reply to Paolo Carlini from comment #10)
> > Maybe Uros can help.
> 
> On an x86 target using the legacy x87 instructions and the 80-bit registers,
> a load of a 64-bit or 32-bit value in memory into the 80-bit registers
> counts as a format conversion and an signaling NaN input will turn into a
> quiet NaN in the register format.

Also, using -msse2 -mfpmath=sse on 32bit target won't help, since 32bit ABI mandates that FP values are returned in x87 register. Your test will work when compiled with "-msse2 -mfpmath=sse -mno-fp-ret-in-387". But note that -mno-fp-ret-in-387 is a compiler option that changes ABI.

The ABI is just wrong for the underlying x87 hardware as far as NaNs are concerned.
Comment 13 Paolo Carlini 2013-07-09 19:43:50 UTC
Thanks Uros, you are providing plenty of detaild which I largely ignored. Thus, realistically, do you think this issue is largely unfixable?
Comment 14 Uroš Bizjak 2013-07-09 20:21:33 UTC
(In reply to Paolo Carlini from comment #13)
> Thanks Uros, you are providing plenty of detaild which I largely ignored.
> Thus, realistically, do you think this issue is largely unfixable?

This issue is unfortunately unfixable. x87 and x86-32 ABI are just not designed to handle all details of IEEE 754 standard.
Comment 15 Paolo Carlini 2013-07-09 20:24:00 UTC
Ok, thanks.
Comment 16 Charles L. Wilcox 2013-07-09 21:27:02 UTC
Created attachment 30487 [details]
Expanded unit-test code to set signaling-NaN values via 'union' aliasing.

Adding code to demonstrate how to generate a signaling-NaN using the existing ABI along with the "return through the x87 register" restriction.
Comment 17 Charles L. Wilcox 2013-07-09 21:29:12 UTC
Okay... so why not avoid the x87 restriction and use aliasing to load the correct value?

I've updated my example to show how I was doing exactly this for some unit-test code I created.
Comment 18 Uroš Bizjak 2013-07-09 21:52:31 UTC
(In reply to Charles L. Wilcox from comment #17)
> Okay... so why not avoid the x87 restriction and use aliasing to load the
> correct value?

Load to x87 stack? The very moment sNaN gets loaded (using fldl or flds) will be converted to qNaN. You can do some trick here (and the compiler can pass FP value in integer registers), but there is no guarantee that the value won't move through x87 stack registers.
Comment 19 Charles L. Wilcox 2013-07-10 11:53:31 UTC
(In reply to Uroš Bizjak from comment #11)
> On an x86 target using the legacy x87 instructions and the 80-bit registers,
> a load of a 64-bit or 32-bit value in memory into the 80-bit registers
> counts as a format conversion and an signaling NaN input will turn into a
> quiet NaN in the register format.

Does this mean if a 80-bit sNaN was generated and loaded into a register it was still have the signaling bit set correctly?  And if so, could this value then be down-converted to a 32 or 64-bit float?  In C++:
    float float32_snan const
      = static_cast< float >( std::numeric_limits< long double >::signaling_NaN() );
    double float64_snan const
      = static_cast< double >( std::numeric_limits< long double >::signaling_NaN() );
Or, is the "cast" here a format conversion, causing the signaling NaN to convert to a quiet NAN?
Comment 20 Uroš Bizjak 2013-07-10 11:58:34 UTC
(In reply to Charles L. Wilcox from comment #19)
> (In reply to Uroš Bizjak from comment #11)
> > On an x86 target using the legacy x87 instructions and the 80-bit registers,
> > a load of a 64-bit or 32-bit value in memory into the 80-bit registers
> > counts as a format conversion and an signaling NaN input will turn into a
> > quiet NaN in the register format.
> 
> Does this mean if a 80-bit sNaN was generated and loaded into a register it
> was still have the signaling bit set correctly?  And if so, could this value

80-bit load is not considered as a format conversion, so signalling bit will be set correctly.

> then be down-converted to a 32 or 64-bit float?  In C++:
>     float float32_snan const
>       = static_cast< float >( std::numeric_limits< long double
> >::signaling_NaN() );
>     double float64_snan const
>       = static_cast< double >( std::numeric_limits< long double
> >::signaling_NaN() );
> Or, is the "cast" here a format conversion, causing the signaling NaN to
> convert to a quiet NAN?

I don't know the c++ details, but compiler can spill the value out from the register stack using 32- or 64-bit float moves. In any case, signalling bit in x87 is unreliable and should not be used.
Comment 21 Andrew Pinski 2016-06-08 17:48:20 UTC
*** Bug 56831 has been marked as a duplicate of this bug. ***