https://gcc.gnu.org/bugzilla/show_bug.cgi?id=128089
Bug ID: 128089
Summary: Wrong code for nested floating-point division in
reassoc with -ffast-math -fno-reciprocal-math
Product: gcc
Version: 17.0
Status: UNCONFIRMED
Severity: normal
Priority: P3
Component: tree-optimization
Assignee: unassigned at gcc dot gnu.org
Reporter: xingyushuai at hygon dot cn
Target Milestone: ---
GCC produces an incorrect result for nested floating-point division when
compiled with -O2 -ffast-math -fno-reciprocal-math. Disabling the reassociation
pass with -fno-tree-reassoc restores
the correct result.
The issue was reproduced on x86-64 using GCC 16.2.0 and GCC trunk 17.0.0
20261021 (experimental) through Compiler Explorer.
Test case (repro.c):
#include <assert.h>
__attribute__ ((noipa)) double
f (double a)
{
return (a / 3) / (a / 6) + a * a;
}
int
main (void)
{
double result = f (6);
assert (result == 38);
return 0;
}
Compile and run:
gcc -O2 -ffast-math -fno-reciprocal-math repro.c -o repro
./repro
Expected behavior: the program exits normally. For a = 6, the expression
evaluates to:
(6 / 3) / (6 / 6) + 6 * 6
= 2 / 1 + 36
= 38
All intermediate values in this calculation are exactly representable in
double. There is no division by zero, overflow, or underflow.
Actual behavior: the assertion fails. An equivalent version that prints
result produces:
expected=38 actual=36.055555555555557
The observed value is consistent with incorrectly transforming:
(a / 3) / (a / 6)
into:
((a / 3) / 6) / a
Adding -fno-tree-reassoc makes the test pass:
gcc -O2 -ffast-math -fno-reciprocal-math \
-fno-tree-reassoc repro.c -o repro
./repro
Compiler Explorer reproduction and comparison
(https://godbolt.org/z/9vecr6rG5) — this version prints the actual result for
inspection.