我偶然发现了一个比较 (==, !=) 浮点类型的有趣案例。 我在将自己的软件从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)。
解决方案:事实证明,您永远不应该相信编译器关于 float 的信息!无论您对所比较的内存有多么确定。一旦数据进入寄存器,它就会发生变化,并且您无法控制它。经过对寄存器的一些挖掘,I found this neat source of information 。希望它对将来的人有用。
最佳答案
浮点 CPU 寄存器可以大于您正在使用的浮点类型。对于通常只有 32 位的 float
来说尤其如此。将使用所有位进行计算,然后将结果四舍五入到最接近的可表示值,然后存储在内存中。
根据内联和编译器优化标志,生成的代码可能会将内存中的一个值与寄存器中的另一个值进行比较。即使它们在内存中的表示形式是逐位相同的,但它们可能会被比较为不相等。
这只是不建议比较浮点值是否相等的众多原因之一。尤其是在您的情况下,它似乎有时有效。
关于c++ - 浮点比较失败且没有任何明显原因(Linux 上的 32 位 X86),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30129989/