7

我有一些简单的代码比较两个浮点值来说明我在 GCC 的优化中看到的问题,并希望有人可以帮助我弄清楚为什么它产生的输出在某些可重复的情况下是不同的。

首先,我知道将浮点值与 == 进行比较是不好的,因为您可以在尾数中偏离非常小的数量,但在我的示例中并非如此。我遇到的问题是基于 2 个因素的输出变化。1)我传入的优化标志,以及 2)如果我取消注释 std::cout 行。

为什么 GCC 生成的代码在 -O2 下运行不同?如果我取消注释打印,为什么在 -O2 下编译的代码可以工作?

这是我正在测试的代码:

#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;
}

当使用 -O0、-O1 和 -O3 ( g++ -O test.cpp ) 在 linux/cygwin 上编译时,我得到以下输出:


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


该输出是正确的,您可以看到浮点数(new_val 和 old_val)的位是相同的。当我使用 -O2 标志( g++ -O2 test.cpp )编译时,我得到以下信息:


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


我会认为这个输出是错误的。即使这两个值按位相同,减去它们和 == 检查表明它们是不同的。如果我然后取消注释 std::cout 行,并使用 -O2 标志( g++ -O2 test.cpp )重建,我会得到以下信息:


$ ./a.exe
使这项工作的随机 COUT
old_val!=new_val
0,40c3126f
diff_val = -6.096
使这项工作的随机 COUT
old_val==new_val
40c3126f,40c3126f
diff_val = 1.19209e-07


这在 old_val == new_val 中是正确的,即使减法仍然显示出细微的差异。

如果英尺为 2000,而不是 20000,则此代码也适用于 -O2。

谁能解释为什么编译的代码会这样?我想知道为什么不能将 2 位相同的浮点值与 == 进行比较。

gcc 版本 3.4.4

4

1 回答 1

12

优化级别和周围的代码可能会影响diff_val计算中使用的值是从内存中还是从寄存器中获取的。处理器可能在一种情况下使用 80 位内部浮点寄存器,在另一种情况下使用来自内存的 32 位浮点值,从而产生意想不到的结果。

==避免使用浮点比较的另一个原因!

于 2010-09-21T23:34:10.313 回答