[Bug c++/116331] New: Math function non-determinism due to excessive precision and constant folding
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=116331 Bug ID: 116331 Summary: Math function non-determinism due to excessive precision and constant folding Product: gcc Version: 14.2.0 Status: UNCONFIRMED Severity: normal Priority: P3 Component: c++ Assignee: unassigned at gcc dot gnu.org Reporter: carl at dehlin dot com Target Milestone: --- Created attachment 58901 --> https://gcc.gnu.org/bugzilla/attachment.cgi?id=58901&action=edit Preprocessed file The following program triggers an assertion when compiled with -std=c++11 -O0 -Wall -Wextra -Wpedantic -Werror -ffloat-store -fexcess-precision=standard -mfpmath=sse #include #include int main() { // Checked with https://www.exploringbinary.com/floating-point-converter/ const double x = 1.2475784324341769870869711667182855308055877685546875; const double y = std::cos(x); const auto f = [x,y] { const double z = std::cos(x); return y == z; }; assert(f()); } See preprocessed file attached. Why do I think this is a bug? = I have read through bug 323 and read through ALL of the duplicate issues and email threads, I am quite familiar with the problem with excessive precision on x86 now. I have added all flags that I have found relevant for this type of issue, which I believe should force the compiler to generate code that does not use excessive precision at all. Yet, the program exhibits behavior which looks like a excessive precision problem. Note that the constant can be stored as a double without a loss of precision (checked online with this tool: https://www.exploringbinary.com/floating-point-converter). Some options are obviously unnecessary when used together, -mfpmath=sse should be enough to force usage of SSE instead of the x87 FPU (please correct me if I have misunderstood this). Some initial analysis = Glancing through the generated assembly it seems that one of the calls to std::cos is compiled as a built in, while the other as a library function call. It could be that the bug lies in the intricate area between the compiler and the standard library. I know that standard library bugs should not be reported as compiler bugs, but with my current level of knowledge of the GCC ecosystem I can not determine where the bugs really stem from. Please advice if I should report this in another place as well. Some ways to fix the code = 1) Manually inline the lambda 2) Remove const qualifier on the x variable 3) Redefine x in the lambda 4) Define x with a macro 5) Add -fno-builtin compiler flag 6) Add -frounding-math compiler flag 7) Increase optimization level Dependencies gmp: 6.3.0 mpfr: 4.2.1 mpc: 1.3.0 flex: 2.6.4-8 (distributed with Ubuntu 23.10) System info === Target: x86_64-pc-linux-gnu Configured with: ../gcc/configure --prefix=/home/cdeln/src/gcc-install --disable-multilib --enable-languages=c,c++,lto gcc version 14.2.0 (GCC) Possibly related issues === https://gcc.gnu.org/bugzilla/show_bug.cgi?id=82318 https://gcc.gnu.org/bugzilla/show_bug.cgi?id=108742 https://gcc.gnu.org/bugzilla/show_bug.cgi?id=114746
[Bug middle-end/116331] Math function non-determinism due to constant folding of cos
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=116331 --- Comment #3 from Carl Dehlin --- Thanks Andrew for your quick response and confirmation of what is going on. After reading through all the excessive precision stuff I was so sure this related to it! Very interesting to see that floating point operations can be non-deterministic in so many ways! Funny thing is that this reduced test fails in a different way from my real test case, which certainly does not fail due to constant folding (if you wonder where I got my magic constant from, the answer is pure randomness!). Still prepping a bug report for that, but struggling with reducing it without triggering this again. I agree, 1 ulp is not very significant, and impressive considering that there are two separate implementations computing almost the same value. The fact that the standard is pushing for constexpr implementations of the math library, does that mean that it will merge into the compiler front end soon? It seems difficult to maintain two separate implementations that most people will expect to work identical. I still think the behavior is very surprising though. I will not attempt to convince anyone that it is a true bug, I'm sure there is a place in the standard somewhere saying that the compiler is allowed to substitute standard library calls to builtin functions with slightly different behavior, and that compile time eval does not have to be bit exact with run time eval. But, I do believe if it is not resolved in some way it might turn into another 323 as more people start to do crazy constexpr stuff with math functions. So I think it is in everyone's interest, both gcc users and devs, to discuss this a bit more. Questions = 1) What benefits does __builtin_cos (i.e. constexpr cos), bring over the library version? If the point of the builtin is just to provide constexpr alternative, then I can just turn all builtin math off and get on with my life. If there are other benefits, then I need to consider them 2) Would it make sense to not promote constant literals produced by math library functions, such that they are only used in explicitly declared constexpr contexts? This depends on the answer to my first question ofc. I think it would reduce future bugs related to constant folding of floating point expressions. 3) Now 1 ulp is not much, but it might also be completely co-incidental, we wouldn't know if there are worse cases without scanning all doubles right? Should one replace == with a typical abs(x-y) < eps sort of thing to handle this? In that case, what should eps be? Everything turns complicated once the == is broken, but I am happy to hear more about this and how to properly approach it, perhaps there are guarantees from GCC, or from the C++ standard itself even. Cheers, / Carl
[Bug middle-end/116331] Math function non-determinism due to constant folding of cos
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=116331 --- Comment #4 from Carl Dehlin --- Found answer or 3) here: https://sourceware.org/glibc/manual/latest/html_mono/libc.html#Known-Maximum-Errors-in-Math-Functions . For x86_64 cos is accurate up to 1 ulp from the correctly rounded value. As I understand the builtin version is always correctly rounded, hence maximum 1 ulp difference.
[Bug middle-end/116331] Math function non-determinism due to constant folding of cos
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=116331 --- Comment #6 from Carl Dehlin --- Thanks Jakub for your explanation. Indeed, the following program runs fine and confirms what you just said. Inspecting the assembly shows can both std::cos and __builtin_cos are substituted to a library call when the input is not known at compile time #include #include #include int main() { double x; if (!scanf("%lf", &x)) { return 1; } const double y = std::cos(x); const auto f = [x,y] { const double z = __builtin_cos(x); __builtin_printf("%a\n%a\n", y, z); return y == z; }; assert(f()); }