x87 floating-point results are effectively unstable due to possible excess precision. Without extra care, this instability can taint everything around and lead to nonsense. Instability is not limited to FP numbers, it extends to integers too: ---------------------------------------------------------------------- #include <stdio.h> __attribute__((noipa,optnone)) // imagine it in a separate TU static int opaque(int i) { return i; } int main() { int z = opaque(1) + 0x1p-60 == 1; printf("z = %d\n", z); if (z) puts("z is one"); } ---------------------------------------------------------------------- $ clang -std=c11 -pedantic -Wall -Wextra -Wno-unknown-attributes -m32 -march=i686 -O3 test.c && ./a.out z = 0 z is one ---------------------------------------------------------------------- clang x86-64 version: clang version 11.0.0 (https://github.com/llvm/llvm-project 14ecbd7b8ded18af6c95f6a9957da541d1ec0e80) ----------------------------------------------------------------------
And instability of integers then easily taints surrounding code: ---------------------------------------------------------------------- #include <stdio.h> __attribute__((optnone)) // imagine it in a separate TU static int opaque(int i) { return i; } static void f(int a) { int z = opaque(0); printf("z = %d\n", z); if (z == a && a == 1) printf("z = %d\n", z); } int main() { f(opaque(1) + 0x1p-60 == 1); } ---------------------------------------------------------------------- $ clang -std=c11 -pedantic -Wall -Wextra -m32 -march=i686 -O3 test.c && ./a.out z = 0 z = 1 ---------------------------------------------------------------------- clang x86-64 version: clang version 11.0.0 (https://github.com/llvm/llvm-project 14ecbd7b8ded18af6c95f6a9957da541d1ec0e80) ---------------------------------------------------------------------- gcc bug -- https://gcc.gnu.org/bugzilla/show_bug.cgi?id=93681
(In reply to Alexander Cherepanov from comment #0) > x87 floating-point results are effectively unstable due to possible excess > precision. Without extra care, this instability can taint everything around > and lead to nonsense. > > Instability is not limited to FP numbers, it extends to integers too: > > ---------------------------------------------------------------------- > #include <stdio.h> > > __attribute__((noipa,optnone)) // imagine it in a separate TU > static int opaque(int i) { return i; } > > int main() > { > int z = opaque(1) + 0x1p-60 == 1; `0x1p-60` there is just a `double` https://godbolt.org/z/mJS3RN It's as expected if it is `long double`: https://godbolt.org/z/NVvJht > printf("z = %d\n", z); > if (z) > puts("z is one"); > } > ---------------------------------------------------------------------- > $ clang -std=c11 -pedantic -Wall -Wextra -Wno-unknown-attributes -m32 > -march=i686 -O3 test.c && ./a.out > z = 0 > z is one > ---------------------------------------------------------------------- > clang x86-64 version: clang version 11.0.0 > (https://github.com/llvm/llvm-project > 14ecbd7b8ded18af6c95f6a9957da541d1ec0e80) > ----------------------------------------------------------------------
(In reply to Roman Lebedev from comment #2) > > int z = opaque(1) + 0x1p-60 == 1; > `0x1p-60` there is just a `double` > https://godbolt.org/z/mJS3RN > It's as expected if it is `long double`: https://godbolt.org/z/NVvJht Yeah, 1 + 0x1p-60 is the simplest expression I could think of where both operands fit into `double` exactly but the result doesn't. x87 evaluates it as `long double` and gets the exact result. But if the result is spilled into memory (as `double`) it's rounded to just 1. If you force the expression to honest `long double` (e.g., by casting any of the operands to it) then everything becomes stable -- the result both in a register and in memory has the same type and the same value. The same happens with SSE -- the result is stored as `double` everywhere and there are no discrepancies.