Comparison of GCC on a float differently at different optimization levels

I have a simple code that compares two float values ​​to illustrate the problem that I see with GCC optimization, and hope someone can help me understand why the output it produces is different in some repeated circumstances.

Firstly, I know that it is bad to compare float values ​​with ==, because you can disconnect from a small amount in the mantissa, however in my example this is not so. The problem I have is the output changes based on two factors. 1) the optimization flag that I pass, and 2) if I uncomment the string std :: cout.

Why is GCC code generated differently with -O2? Why does code compiled under -O2 work if I uncomment the print?

Here is the code I'm testing:

#include <iostream> const float ft_to_m = (float)0.3048; const float m_to_ft = (float)3.28083989501; float FeetToKilometers( float & Feet ) { float Kilometers; Kilometers = (ft_to_m * Feet) / 1000.; return Kilometers; } int main(void) { float feet = 20000.; float old_val = 0; float new_val = FeetToKilometers(feet ); float diff_val = 0; int *old_int = reinterpret_cast<int*>(&old_val); int *new_int = reinterpret_cast<int*>(&new_val); for (int i=0; i<2; i++) { new_val = FeetToKilometers(feet ); diff_val = old_val-new_val; //std::cout << "Random COUT that makes this work" << std::endl; if(old_val==new_val) { std::cout << "old_val==new_val" << std::endl; std::cout << std::hex << *old_int << "," << std::hex << *new_int << std::endl; std::cout << "diff_val = " << diff_val <<std::endl; } else { std::cout << "old_val!=new_val" <<std::endl; std::cout << std::hex << *old_int << "," << std::hex << *new_int << std::endl; std::cout << "diff_val = " << diff_val <<std::endl; old_val=FeetToKilometers(feet); } } return 0; } 

When compiling in linux / cygwin with -O0, -O1 and -O3 (g ++ -O test.cpp), I get the following output:


$. / a.exe
old_val! = new_val
0.40c3126f
diff_val = -6.096
old_val == new_val
40c3126f, 40c3126f
diff_val = 0


This conclusion is correct, you can see that the bits for float (new_val and old_val) are identical. When I compile the -O2 flag (g ++ -O2 test.cpp), I get the following:


$. / a.exe
old_val! = new_val
0.40c3126f
diff_val = -6.096
old_val! = new_val
40c3126f, 40c3126f
diff_val = 1.19209e-07


I believe that this result is erroneous. Despite the fact that the two values ​​are the same bits, subtracting them and checking == show that they are different. If I then uncomment the std :: cout line and rebuild with the -O2 flag (g ++ -O2 test.cpp), I get the following:


$. / a.exe
Random COUT that does this job
old_val! = new_val
0.40c3126f
diff_val = -6.096
Random COUT that does this job
old_val == new_val
40c3126f, 40c3126f
diff_val = 1.19209e-07


This is true that old_val == new_val, although subtraction still has a slight difference.

This code also works under -O2 if kicking 2000, not 20,000.

Can someone explain why compiled code behaves like this? I want to know why 2-bit identical float values ​​cannot be compared with ==.

gcc version 3.4.4

+6
gcc equals floating-point linux mantissa
source share
1 answer

The optimization level and the surrounding code can affect whether the values ​​used in diff_val are retrieved from memory or from registers. A processor can use 80-bit internal floating point registers in one case and 32-bit floating point values ​​from memory in another case, giving unexpected results.

Another reason to avoid using == for floating point comparisons!

+10
source share

All Articles