没有任何明显原因的浮点数比较失败(Linux 上的 32 位 X86)

Float comparisons failing without any obvious reason (32-bit X86 on Linux)

我偶然发现了一个比较 (==, !=) 浮点类型的有趣案例。 我在将自己的软件从 windows 移植到 linux 时遇到了这个问题。这有点令人失望。相关代码如下:

template<class T> class PCMVector2 {
public:
   T x, y;

public:
   bool operator == ( const PCMVector2<T>& a ) const {
       return x == a.x && y == a.y;
   }
   bool operator != ( const PCMVector2<T>& a ) const {
       return x != a.x || y != a.y;
   }
   // Mutable normalization
   PCMVector2<T>& Normalize() { 
      const T l = 1.0f / Length();
      x *= l;
      y *= l;
      return *this;
   }
   // Immutable normalization
   const PCMVector2<T> Normalized() { 
      const T l = 1.0f / Length();
      return PCMVector2<T>(x*l,y*l);
   }
   // Vector length
   T Length() const { return sqrt(x*x+y*y); }
};

我巧妙地设计了一个单元测试函数,它在移植到 linux 之前检查与那些 类 有关的所有可用功能。而且,与 msvc 相比,g++ 不会报错,但会在运行时给出不正确的结果。

我被难住了,所以我做了一些额外的日志记录、双关语、memcmp 等,它们都显示内存 1:1 是一样的!有人对此有任何想法吗?

我的旗帜是:-Wall -O2 -j2

提前致谢。

EDIT2:失败的测试用例是:

vec2f v1 = vec2f(2.0f,3.0f);
v1.Normalize(); // mutable normalization
if( v1 != vec2f(2.0f,3.0f).Normalized() ) //immutable normalization
    // report failure

注意:两种规范化相同,并且产生相同的结果(根据 memcmp)。

解决方案:事实证明,您永远不应该相信编译器有关浮点数的信息!无论您对比较的内存有多确定。一旦数据进入寄存器,它就会改变,而你无法控制它。在对寄存器进行一些挖掘之后,I found this neat source of information。希望对以后的人有用。

浮点 CPU 寄存器可以大于您正在使用的浮点类型。对于通常只有 32 位的 float 尤其如此。将使用所有位进行计算,然后将结果四舍五入为最接近的可表示值,然后存储在内存中。

根据内联和编译器优化标志,生成的代码可能会将内存中的一个值与寄存器中的另一个值进行比较。那些可能比较不相等,即使它们在内存中的表示将是逐位相同的。

这只是不推荐比较浮点值是否相等的众多原因之一。尤其是在您的情况下,有时