GCC Handling Float comparison differently at different Optimization Levels
开发者_如何学运维I have some simple code that is comparing two float values to illustrate a problem I see with GCC's optimization and am hoping someone can help me figure out why the output it produces is different under some repeatable circumstances.
First, I know it's bad to compare float values with == because you can be off by some very small amount in the mantissa, however that is not the case in my example. The problem I have is the output changes based on 2 factors. 1) the optimization flag I pass in, and 2) if I uncomment the std::cout line.
Why does the code GCC produce run differently under -O2? Why does the code compiled under -O2 work if I uncomment the print?
Here is the code I am 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 compiled on 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
That output is correct, you can see the bits for the floats (new_val and old_val) are identical. When I compile with 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 would consider this output wrong. Even though the two values are the same bit wise, subtracting them and the == check indicate 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 makes this work old_val!=new_val 0,40c3126f diff_val = -6.096 Random COUT that makes this work old_val==new_val 40c3126f,40c3126f diff_val = 1.19209e-07
This is correct in that old_val == new_val, even though the subtraction still shows a slight difference.
This code also works under -O2 if feet is 2000, instead of 20000.
Can anyone explain why the compiled code is behaving like this? I want to know why 2 bit identical float values cannot be compared with ==.
gcc version 3.4.4
The optimization level and surrounding code may affect whether the values used in the diff_val
calculation are being fetched from memory, or from registers. The processor
may be using 80-bit internal floating point registers in one case, and 32-bit floating
point values from memory in the other case, giving unexpected results.
Yet another reason to avoid using ==
for floating point comparisons!
精彩评论