由于gcc
选项-ffast-math
有效地禁用了NaN
和-/+inf
,我正在寻找可能是我的性能关键数学代码中代表NaN
的下一个最佳选择。理想情况下,如果对(add,mul,div,sub等)进行操作的前哨值将产生NaN
会做的哨兵值,但我怀疑这是可能的,因为我认为NaN
是唯一能够实现这一点的值。 -0.0
可能不太适合,因为它在-ffast-math
也被禁用,可能会阻止某些优化,如(x+0.0)
等。
也许我的问题应该是,有没有办法使用NaN
或其他一些“特殊的双重”,同时能够启用大量的数学优化而不会崩溃?
系统是Linux/x64, gcc 4.8.1
。
如果您正在寻找一个可以通过算术运算传播的值,NaN
仍然可以选择-ffast-math
。问题出在其他地方。使用-ffast-math
,由于优化,一些操作可以从计算中移除,然后无法保证NaN
或任何其他值将被传播。
例如,以下,使用-ffast-math
设置,将导致硬编写0.0
到n
并且n
没有特殊的价值可以保护它。
float n = NAN;
n *= 0.0;
你可以做的一件事就是使用-fno-finite-math-only -ftrapping-math
和-ffast-math
,Shafik Yaghmour说。另一方面,如果只有少数几个地方你期望一个不好的价值,你可以自己检查它,把测试完全放在那些点上。
我能想到的最后一个选项 - 如果你真的非常需要优化 - 是手动将NaN
(可能是inf
)值注入计算并检查它传播的时间。然后在那些传播停止的地方,测试NaN
(inf
)的发生。 - 这是一种不安全的方法,因为我不是百分百肯定,-ffast-math
可以涉及有条件的操作流程。如果可以的话,这个解决方案很有可能无效。所以它有风险,如果选择需要非常繁重的测试,涵盖计算的所有分支。
通常我会反对最后的解决方案,但实际上有机会,NaN
(inf
)值将通过整个计算或几乎整体传播,因此它可以提供您正在寻求的性能。所以你可能想承担风险。
正如Shafik Yaghmour所说,用NaN
检查-ffast-math
你能做什么
inline int isnan(float f)
{
union { float f; uint32_t x; } u = { f };
return (u.x << 1) > 0xff000000u;
}
和double
inline int isnan(double d)
{
union { double d; uint64_t x; } u = { d };
return (u.x << 1) > 0xff70000000000000ull;
}
检查inf
将是
inline int isinf(float f)
{
union { float f; uint32_t x; } u = { f };
return (u.x << 1) == 0xff000000u;
}
inline int isinf(double d)
{
union { double d; uint64_t x; } u = { d };
return (u.x << 1) == 0xff70000000000000ull;
}
你也可以合并isnan
和isinf
。