Bug 114050 - Inconsistency in double/float constant evaluation between 32 and 64 bit
Summary: Inconsistency in double/float constant evaluation between 32 and 64 bit
Status: RESOLVED DUPLICATE of bug 92875
Alias: None
Product: gcc
Classification: Unclassified
Component: c++ (show other bugs)
Version: 13.2.0
: P3 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2024-02-22 10:02 UTC by Søren Jæger Hansen
Modified: 2024-04-16 18:02 UTC (History)
3 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed:


Attachments
The test application (434 bytes, text/plain)
2024-02-22 10:02 UTC, Søren Jæger Hansen
Details
Preprocessed output (gzipped to fit) (174.23 KB, application/x-gzip)
2024-02-22 10:04 UTC, Søren Jæger Hansen
Details

Note You need to log in before you can comment on or make changes to this bug.
Description Søren Jæger Hansen 2024-02-22 10:02:56 UTC
Created attachment 57491 [details]
The test application

In GCC version 13 and later, we've found some inconsistency in the way the compiler calculates double constants in our code. We originally discovered this in a rounding unit test in our code, and have extracted the specific inconsistency in attached double-test.cpp.

When compiling in default 64-bit mode on the x86 platform, we get the consistent result of previous GCC versions (the output from the test code is the same constant double value calculated in three different ways, and dumped as hex as well):

g++ -std=c++23 -m64 -o double-test double-test.cpp
./double-test
Output:
GCC version: 13.2.0
a =    -5.42101e-20 ( 00 00 00 00 00 00 f0 bb )
b =    -5.42101e-20 ( 00 00 00 00 00 00 f0 bb )
c =    -5.42101e-20 ( 00 00 00 00 00 00 f0 bb )

However, compiling the same application in 32-bit mode yields a different result:

g++ -std=c++23 -m32 -o double-test double-test.cpp
./double-test
Output:
GCC version: 13.2.0
a =    -4.06576e-20 ( 00 00 00 00 00 00 e8 bb )
b =               0 ( 00 00 00 00 00 00 00 00 )
c =    -4.06576e-20 ( 00 00 00 00 00 00 e8 bb )

If using gnu extensions as the selected standard, we're back to the old, consistent behavior, also for 32 bits:

g++ -std=gnu++23 -m32 -o double-test double-test.cpp
./double-test
Output:
GCC version: 13.2.0
a =    -5.42101e-20 ( 00 00 00 00 00 00 f0 bb )
b =    -5.42101e-20 ( 00 00 00 00 00 00 f0 bb )
c =    -5.42101e-20 ( 00 00 00 00 00 00 f0 bb )

We have only seen the problem in 32 bit mode with one of the strict c++ standards selected (-std=c++*), not with gnu extensions (-std=gnu++*).

Testing various versions on the Compiler Explorer site, the problem seems to occur as of version 13 of GCC.


g++ -v output:
Using built-in specs.
COLLECT_GCC=g++
COLLECT_LTO_WRAPPER=/usr/local/schilling/gcc-13.2.0/libexec/gcc/x86_64-pc-linux-gnu/13.2.0/lto-wrapper
Target: x86_64-pc-linux-gnu
Configured with: ../gcc-13.2.0/configure --prefix=/usr/local/schilling/gcc-13.2.0 --enable-shared --enable-threads=posix --enable-__cxa_atexit --enable-clocale=gnu --enable-languages=c,c++ --enable-multilib --disable-bootstrap --with-system-zlib
Thread model: posix
Supported LTO compression algorithms: zlib
gcc version 13.2.0 (GCC)
Comment 1 Søren Jæger Hansen 2024-02-22 10:04:16 UTC
Created attachment 57492 [details]
Preprocessed output (gzipped to fit)
Comment 2 Andrew Pinski 2024-02-22 10:05:47 UTC
This is the expected behavior. Due to excessive precision for x87.
Comment 3 Andrew Pinski 2024-02-22 10:06:20 UTC
See PR 323 and other related bug reports.
Comment 4 Sam James 2024-02-22 10:06:33 UTC
(and -mfpmath=sse works.)
Comment 5 Andrew Pinski 2024-02-22 10:08:26 UTC
`0.0003 - 0.0001*3.0` with -std=c++23 is done all in long double and then casted to double. Which is why b is 0 there.
Comment 6 Andrew Pinski 2024-02-22 10:10:58 UTC
See explicitly pr 93112
Comment 7 Jonathan Wakely 2024-02-22 12:21:29 UTC
And the first item at https://gcc.gnu.org/bugs/#nonbugs
Comment 8 Søren Jæger Hansen 2024-02-22 12:28:57 UTC
I get all this, and thanks for quick processing. Still I think it's a bit odd that -std=c++* and -std?=gnu++* gives different results, but I assume there's a good reason for that.

We'll be using -std=gnu++23 for now and move to 64 bit later as a solution for us.
Comment 9 Jonathan Wakely 2024-02-22 12:35:14 UTC
See the first item at https://gcc.gnu.org/gcc-13/changes.html#cxx
Comment 10 Søren Jæger Hansen 2024-02-22 12:39:51 UTC
-fexcess-precision=fast it is for now then, thanks again for fast feedback.
Comment 11 Andrew Pinski 2024-04-16 12:47:41 UTC
.

*** This bug has been marked as a duplicate of bug 92875 ***
Comment 12 Vincent Lefèvre 2024-04-16 14:11:06 UTC
(In reply to Søren Jæger Hansen from comment #10)
> -fexcess-precision=fast it is for now then, thanks again for fast feedback.

-fexcess-precision is unrelated. Its goal is to choose whether GCC conforms to the standard, i.e. reduces the precision for assignments and casts (*only* in these cases, thus constants are not concerned), or generates faster but non-conforming code.
Comment 13 Jonathan Wakely 2024-04-16 15:02:21 UTC
-fexcess-precision does affect constants.

With -fexcess-precision=standard, assignments and casts discard excess precision which may otherwise be present in arithmetic expressions and constants. With -fexcess-precision=fast the excess precision might be retained even after casts and assignments, or it might be discarded at other points.

But in both cases, a floating constant might have excess precision. Whether that excess precision is discarded, and when it's discarded, is affected by -fexcess-precision.
Comment 14 Vincent Lefèvre 2024-04-16 17:05:59 UTC
This bug is about "double/float constant evaluation" (and it has been marked as a duplicate of a bug precisely on this subject), not about the rules that are applied *after* this evaluation.
Comment 15 Jakub Jelinek 2024-04-16 17:10:55 UTC
There is no bug, the compiler implements what the standard says for the FLT_EVAL_METHOD == 2 case.
If you want in that mode a constant guaranteed to be in double precision, you need to explicitly cast the constant to double, otherwise it will have long double precision with type of double and that extra precision is only rounded to double precision on casts and assignments.
Comment 16 Vincent Lefèvre 2024-04-16 17:32:40 UTC
(In reply to Jakub Jelinek from comment #15)
> There is no bug, the compiler implements what the standard says for the
> FLT_EVAL_METHOD == 2 case.

I agree. I meant this *invalid* bug.
Comment 17 Vincent Lefèvre 2024-04-16 18:01:42 UTC
(In reply to Jonathan Wakely from comment #13)
> -fexcess-precision does affect constants.

Indeed, and this is a bug, as -fexcess-precision=fast was not meant to make general programs less accurate (but to possibly keep more precision internally, avoiding costly conversions, even in cases where this is forbidden). See bug 114746.
Comment 18 Andrew Pinski 2024-04-16 18:02:29 UTC
(In reply to Vincent Lefèvre from comment #17)
> (In reply to Jonathan Wakely from comment #13)
> > -fexcess-precision does affect constants.
> 
> Indeed, and this is a bug, as -fexcess-precision=fast was not meant to make
> general programs less accurate (but to possibly keep more precision
> internally, avoiding costly conversions, even in cases where this is
> forbidden). See bug 114746.

No that is NOT true.