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

我偶然发现了一个比较(==,!=)浮点types的有趣案例。 我在将自己的软件从Windows移植到Linux时遇到了这个问题。 这是一个无聊的一点。 相关的代码如下:

template<class T> class PCMVector2 { public: T x, y; public: bool operator == ( const PCMVector2<T>& a ) const { return x == ax && y == ay; } bool operator != ( const PCMVector2<T>& a ) const { return x != ax || y != ay; } // 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); } }; 

我巧妙地devise了一个unit testingfunction,在移植到linux之前,检查所有可用的function。 而且,与msvc相比,g ++不会发出抱怨,但在运行时会产生不正确的结果。

我被难住了,所以我做了一些额外的日志logging,types双关语,memcmp的等,他们都表明,内存是1:1相同! 任何人有任何想法呢?

我的标志是: -Wall -O2 -j2

提前致谢。

编辑2 :失败的testing案例是:

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

注意:两个标准化都是相同的,并且产生相同的结果(根据memcmp)。

解决scheme :原来,你永远不要相信编译器有关浮动数字! 不pipe你有多确定你所比较的记忆。 一旦数据进入寄存器,它可以改变,你无法控制它。 在对寄存器进行了一些挖掘之后, 我发现了这个简洁的信息来源 。 希望对未来的人有用。

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

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

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