我的代码中有以下函数检查数字是否具有允许的值(在日志空间中):
template<class T>
static void check_if_normal(T t)
{
T additive_neutral_element = make_additive_neutral_element<T>();
// probability is allowed to be 0 in logspace
// probability also is allowed to be -inf in logspace
if (!std::isnormal(t) && t != 0 && t != additive_neutral_element)
throw std::underflow_error(
"Probability of " + std::to_string(t) +
" is abnormal --- possible cause underflow.");
}
在使用此函数的上下文中,我专门使用长双精度数。当我在没有 valgrind 的情况下运行我的程序时,一切正常,但是当我使用 valgrind 运行它时,该函数实际上引发了异常。我怀疑 valgrind 做了一些改变长双打的格式或类似的事情。我找到了这个:
Valgrind 在实现 x86/AMD64 浮点时相对于 IEEE754 有以下限制。
精度:不支持 80 位算术。在内部,Valgrind 以 64 位表示所有此类“long double”数字,因此结果可能存在一些差异。这是否至关重要还有待观察。请注意,x86/amd64 fldt/fstpt 指令(读/写 80 位数字)是正确模拟的,使用与 64 位之间的转换,因此如果有人想看,80 位数字的内存图像看起来是正确的。
从许多 FP 回归测试中观察到的印象是准确性差异并不显着。一般来说,如果一个程序依赖于 80 位精度,那么将其移植到仅支持 64 位 FP 精度的非 x86/amd64 平台可能会有困难。即使在 x86/amd64 上,程序也可能得到不同的结果,具体取决于它是编译为使用 SSE2 指令(仅限 64 位)还是 x87 指令(80 位)。最终效果是使 FP 程序的行为就好像它们在具有 64 位 IEEE 浮点数的机器上运行一样,例如 PowerPC。在 amd64 上,FP 算法默认在 SSE2 上完成,因此从 FP 的角度来看,与 x86 相比,amd64 看起来更像 PowerPC,并且与 x86 相比,明显的精度差异要少得多。
舍入:Valgrind 确实遵守 4 种 IEEE 规定的舍入模式(到最接近、到 +infinity、到 -infinity、到零),用于以下转换:浮点到整数,整数到浮点,可能会丢失精度,以及浮点到浮点舍入。对于所有其他 FP 操作,仅支持 IEEE 默认模式(四舍五入)。
FP 代码中的数值异常:IEEE754 定义了五种可能发生的数值异常:无效操作(负数的 sqrt 等)、除以零、上溢、下溢、不精确(精度损失)。
对于每个异常,IEEE754 定义了两个操作过程:(1) 可以调用用户定义的异常处理程序,或者 (2) 定义默认操作,“修复”并允许计算在没有抛出异常。
目前 Valgrind 只支持默认的修复动作。再次感谢有关异常支持重要性的反馈。
当 Valgrind 检测到程序试图超过任何这些限制(设置异常处理程序、舍入模式或精度控制)时,它可以打印一条消息,追溯发生的位置,并继续执行。此行为曾经是默认行为,但消息很烦人,因此默认情况下显示它们现在已禁用。使用 --show-emwarns=yes 来查看它们。
上述限制精确地定义了 IEEE754 的“默认”行为:所有异常的默认修复、四舍五入到最近的操作和 64 位精度。
http://www.valgrind.org/docs/manual/manual-core.html#manual-core.limits
但我不确定这是否适用。Valgrind 并没有像引用中所说的那样打印出提供追溯的消息。它打印了这个:
terminate called after throwing an instance of 'std::underflow_error'
what(): Probability of -nan is abnormal --- possible cause underflow.
==4899==
==4899== Process terminating with default action of signal 6 (SIGABRT)
==4899== at 0x5710428: raise (raise.c:54)
==4899== by 0x5712029: abort (abort.c:89)
==4899== by 0x4EC984C: __gnu_cxx::__verbose_terminate_handler() (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21)
==4899== by 0x4EC76B5: ??? (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21)
==4899== by 0x4EC7700: std::terminate() (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21)
==4899== by 0x4EC7918: __cxa_throw (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21)
顺便提一句。如果这与导致此行为的原因有关,我将g++ (Ubuntu 5.4.0-6ubuntu1~16.04.4) 5.4.0 20160609
在 64 位系统上使用。
上面的引用可能是我观察到这一点的原因吗?如果不是,还有什么可能是原因?