我们有用 C++ 编写的数字代码。很少但在某些特定输入下,某些计算会产生“nan”值。
当某个数值计算导致生成“nan”时,是否有标准或推荐的方法可以停止并提醒用户?(在调试模式下)。考虑到矩阵和向量的巨大大小,检查每个结果是否等于“nan”似乎是不切实际的。
标准数值库如何处理这种情况?你能对此有所了解吗?
我们有用 C++ 编写的数字代码。很少但在某些特定输入下,某些计算会产生“nan”值。
当某个数值计算导致生成“nan”时,是否有标准或推荐的方法可以停止并提醒用户?(在调试模式下)。考虑到矩阵和向量的巨大大小,检查每个结果是否等于“nan”似乎是不切实际的。
标准数值库如何处理这种情况?你能对此有所了解吗?
当应用于数值运算时,NaN 会被传播。因此,检查最终结果是否为 NaN 就足够了。至于如何做——如果为 >= C++11 构建,则有 std::isnan,正如 Goz 所注意到的。对于 < C++11 - 如果想要防弹 - 我会亲自进行位检查(特别是,如果可能涉及优化)。NaN 的模式是
? 11.......1 xx.......x
sign bit ^ ^exponent^ ^fraction^
在哪里?可以是任何东西,并且至少有一个x必须是 1。
对于平台相关的解决方案,似乎还有另一种可能性。glibcfeenableexcept
中有一个函数(可能带有函数和编译器选项) ,当发生无效的浮点操作时,它会打开生成的正弦波。还有函数(可能带有函数和编译器选项),它在VC中几乎相同。signal
-fnon-call-exceptions
SIGFPE
_control87
_set_se_translator
/EHa
尽管这是最初来自 glibc 的非标准扩展,但在许多系统上,您可以使用feenableexcept
声明的例程<fenv.h>
来请求机器捕获特定的浮点异常并传递SIGFPE
给您的进程。您可以使用fedisableexcept
来屏蔽陷阱,并fegetexcept
查询未屏蔽的异常集。默认情况下,它们都被屏蔽。
在没有这些例程的旧 BSD 系统上,您可以使用fpsetmask
and fpgetmask
from<ieeefp.h>
代替,但世界似乎正在融合 glibc API。
警告:glibc 当前有一个错误(C99 标准例程)fegetenv
具有屏蔽 x86 上所有异常陷阱的意外副作用,因此您必须在fesetenv
之后调用以恢复它们。(向您展示任何人对这些东西的依赖程度......)
在许多体系结构上,您可以取消屏蔽无效异常,当 NaN 通常由诸如0*infinity
. 在调试器中运行,您将中断此中断并检查导致该点的计算。在调试器之外,您可以安装陷阱处理程序来记录有关产生无效操作的计算状态的信息。
例如,在 x86 上,您将清除 FPCR(位 0)和 MXCSR(位 7)中的无效操作掩码位,以分别启用对来自 x87 和 SSE 操作的无效操作的捕获。
一些单独的平台提供了一种从 C 语言写入这些控制寄存器的方法,但是没有可跨平台工作的可移植接口。
测试 f!=f 可能会在启用 -ffast-math 优化的情况下使用 g++ 出现问题:Checking if a double (or float) is NaN in C++
唯一万无一失的方法是检查位模式。
至于在哪里实施检查,这实际上取决于您的计算细节以及 Nan 错误的频率,即持续污染计算与在某些阶段进行检查的性能损失。