0

I'm developing a program in C++. I have to perform some calculations with pow of math library. When a run this code:

#include <math.h>
#include <stdio.h>

int main (void)
{
unsigned long l;
double d1,d2,d3;

l = 13831305406817642647;
d1 = 2.;
d2 = *((double*)(&l));
d3 = pow(2.,d2);
printf ("%lu,%20.20f\n",*((unsigned long *)(&d1)),d1);
printf ("%lu,%20.20f\n",*((unsigned long *)(&d2)),d2);
printf ("%lu,%20.20f\n",*((unsigned long *)(&d3)),d3);

return 0;
}

With Fedora 35 I obtain:

4611686018427387904,2.00000000000000000000
13831305406817642647,-1.16674465427289475450
4601695688420081959,0.44542528011426657519

But with Fedora 36:

4611686018427387904,2.00000000000000000000
13831305406817642647,-1.16674465427289475450
4601695688420081958,0.44542528011426651968

I converted the doubles to a binary image of unsigned long to detect any change for just one bit.

In the third line you can observe this kind of change. Why I obtain a different result when I calculate over the same values with pow?

The full information about the version of Fedora (obtained with uname -r):

For Fedora 35: 5.17.11-200.fc35.x86_64

For Fedora 36: 5.17.0-0.rc7.116.fc36.x86_64

Any help will be welcome.

phuclv
  • 37,963
  • 15
  • 156
  • 475
Felix
  • 1
  • 2
    `*((unsigned long *)(&d1))` violates strict aliasing rule and invokes UB – phuclv Jun 08 '22 at 04:14
  • 1
    In addition to all of this being UB due to aliasing violation, there is also no inherit guarantee that the math functions will always output the same values. They will give some output close to the mathematical exact result with an error determined by the particular implementation. You'll have to read the documentation of the C standard library implementation to figure out what guarantees they make. And if the compiler does it at compile-time this may differ as well. – user17732522 Jun 08 '22 at 05:40
  • 1
    Are you already aware of https://stackoverflow.com/questions/588004/is-floating-point-math-broken ? Consider [using](https://godbolt.org/z/zEzY18xc3) the `%a` format specifier (or [`std::hexfloat`](https://en.cppreference.com/w/cpp/io/manip/fixed), since you tagged this question as C++). – Bob__ Jun 08 '22 at 09:01

1 Answers1

1

I used x86-64 gcc 12.1 to compile your code twice in Compiler Explorer: https://godbolt.org/z/47K94dMe3. The only difference was the optimization level, which caused the outputs to differ.

In the test, -O1 enabled the compiler to calculate the d3 during compilation and pass 4601695688420081958 directly to printf().

Interestingly, x86-64 clang is able to compute d3 with -O1, and the result is 4601695688420081959 (the same as pow(2.,d2)).

Basically, compilers will remove the use of pow() if optimization level is not -O0, and a constant calculation is viable. I guess gcc uses its own method to compute pow(), while clang calls pow() or uses a similar approach to get the answer. You can use objdump to get assembly code from your executables to check the details.

Simon Smith
  • 414
  • 3
  • 6
  • Thanks for your answer. Although I have to work with objdump, as you said, I ran the same code compiled with the same options (including the optimization level), and the result remains different. So, I don't think the problem is a difference of behavior between the compiler and the library. – Felix Jun 09 '22 at 10:02