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

时间:2015-05-08 17:56:44

标签: c++ linux floating-point vectorization

我偶然发现了一个比较(==,!=)浮点类型的有趣案例。 我将自己的软件从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。希望将来对某人有用。

1 个答案:

答案 0 :(得分:4)

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

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

这只是为什么不建议比较浮点值是否相等的众多原因之一。特别是在你的情况下,它似乎在某些时候工作