如果更喜欢使用 -ffast-math,则为双倍的良好哨兵价值
Posted
技术标签:
【中文标题】如果更喜欢使用 -ffast-math,则为双倍的良好哨兵价值【英文标题】:Good sentinel value for double if prefer to use -ffast-math 【发布时间】:2013-07-10 23:56:57 【问题描述】:由于gcc
选项-ffast-math
有效地禁用了NaN
和-/+inf
,我正在寻找在我的性能关键数学代码中表示NaN
的下一个最佳选项。理想情况下,如果对(add、mul、div、sub 等)进行操作,sentinel 值会像NaN
那样产生sentinel 值,但我怀疑这是可能的,因为我认为NaN
是唯一可以完成的值这。 -0.0
可能不太适合,因为它在 -ffast-math
中也被禁用,并且可能会阻止某些优化,例如 (x+0.0)
等。
也许我的问题应该是,有没有什么方法可以使用NaN
或其他一些“特殊双精度”,同时能够在不崩溃的情况下启用大量数学优化?
系统是Linux/x64, gcc 4.8.1
。
【问题讨论】:
这个帖子可能会有所帮助Mingw32 std::isnan with -ffast-math @ShafikYaghmour 谢谢,这个帖子很有帮助。 还有使用-fno-finite-math-only -ftrapping-math
lua-users.org/lists/lua-l/2009-04/msg00091.html的选项
我同样想知道返回浮点值的函数的良好哨兵。我决定使用optional
,如optional<float>
或optional<double>
。除了明确意图之外,我认为这还具有对快速数学等编译器选项不太敏感的优点。这当然需要 C++17,或者使用 boost::optional
或编写自己的 optional
实现(这是我所做的,这对我来说似乎很容易)。
【参考方案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
) 值将通过整个计算或几乎整个传播,因此它可以提供您正在寻求的性能.所以你可能想冒险。
使用-ffast-math
检查NaN
,正如Shafik Yaghmour 所说,使用
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
。
【讨论】:
上面的isnan和isinf应该是0xffe0000000000000ull而不是0xff70000000000000ull。以上是关于如果更喜欢使用 -ffast-math,则为双倍的良好哨兵价值的主要内容,如果未能解决你的问题,请参考以下文章
有啥理由更喜欢 System.arraycopy() 而不是 clone()?
Entitymanager.flush() VS EntityManager.getTransaction().commit - 我应该更喜欢啥?