Valgrind导致长双打数字问题

时间:2017-05-21 21:37:49

标签: c++ valgrind numeric long-double

我的代码中有以下函数,用于检查数字是否具有允许值(在日志空间中):

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.");
}

在使用此功能的上下文中,我使用long double。当我运行没有valgrind的程序时,一切正常,但是当我用valgrind运行它时,该函数实际上引发了异常。我怀疑valgrind会做一些改变长双打或其他方式的东西。我发现了这个:

  

Valgrind在实现相对于IEEE754的x86 / AMD64浮点时存在以下限制。

     

精度:不支持80位运算。在内部,Valgrind以64位表示所有这样的“长双”数,因此结果可能存在一些差异。这是否至关重要还有待观察。注意,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角度来看amd64看起来更像是PowerPC而不是x86,并且与x86相比,精确度差异要小得多。

     

舍入:Valgrind确实观察了4个IEEE规定的舍入模式(最近,+无穷大,无穷大,为零)以进行以下转换:浮点数到整数,整数浮点数可能会丢失精度和浮动到浮动的圆角。对于所有其他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)

顺便说一句。我在64位系统上使用g++ (Ubuntu 5.4.0-6ubuntu1~16.04.4) 5.4.0 20160609,如果这与导致此行为的原因相关。

以上引用可能是我观察此事的原因,如果不是原因还有什么原因?

1 个答案:

答案 0 :(得分:-1)

我不知道原因,但这可能会有所帮助。

  

To work around this, use CPPDEFINES=BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS when compiling