最近编译protobuf遇到一个奇葩事儿,修改了一下编译优化选项,把-O3改成了-Ofast,居然程序运行的结果还不一致了,这让我非常意外。找来找去,发现是NaN在搞鬼。
NaN对于float或者double是一个特殊的存在,如何判断一个float/double是不是NaN呢,方法也很特别,就是NaN跟NaN是不相等的,就像无穷大不能比大小一样。所以实现是这样的:
inline bool IsNaN(double value) {
// NaN is never equal to anything, even itself.
return value != value;
}
在math.h里也有这样一个函数叫isnan(),估计实现也是一个原理。有趣的是,这种写法,估计就把编译弄晕了,导致一优化结果就不对了。
#include <math.h>
#include <iostream>
#include <stdio.h>
int main()
{
float f = NAN;
printf("%f %d %d\n", f, isnan(f), f == f);
return 0;
}
上面的代码执行结果就很奇葩:
nan 1 0 //-O0
nan 1 0 //-Os
nan 0 1 //-Ofast
nan 1 0 //-O3
通常来说,我们把-Ofast认为是专门考虑性能的优化。很奇怪的是,它这结果跟-O3反而不同的,网上有个说法是,-Ofast使用了FastMath,就导致为了计算速度不那么精准了。