C++ C+中浮点溢出的诊断+;程序

C++ C+中浮点溢出的诊断+;程序,c++,floating-point,double,overflow,numeric,C++,Floating Point,Double,Overflow,Numeric,我遇到过这样一种情况:一些数值结果(包括带有double和float的浮点运算)对于较大的输入大小不正确,但对于较小的输入大小则不正确 一般来说,我想知道有哪些工具可用于诊断诸如数值溢出和有问题的精度损失等情况 换句话说:是否有工具投诉溢出等。valgrind投诉内存错误的方式也一样?如果启用浮点异常,则FPU可以在溢出时引发异常。具体工作方式取决于操作系统。例如: 在Windows上,可以使用un掩蔽yEMuleOp溢出,以便在溢出时得到C++异常。 在Linux上,您可以使用启用FE_溢出

我遇到过这样一种情况:一些数值结果(包括带有
double
float
的浮点运算)对于较大的输入大小不正确,但对于较小的输入大小则不正确

一般来说,我想知道有哪些工具可用于诊断诸如数值溢出和有问题的精度损失等情况


换句话说:是否有工具投诉溢出等。valgrind投诉内存错误的方式也一样?

如果启用浮点异常,则FPU可以在溢出时引发异常。具体工作方式取决于操作系统。例如:

    在Windows上,可以使用un掩蔽yEMuleOp溢出,以便在溢出时得到C++异常。
  • 在Linux上,您可以使用启用FE_溢出上的异常,以便在溢出时获得异常。例如,要启用所有异常,请在
    main
    中调用
    feenableexcept(FE\u all\u EXCEPT)
    。要启用溢出并除以零,请调用
    feenableexcept(FE_overflow | FE_DIVBYZERO)
请注意,在所有情况下,第三方代码可能会禁用您启用的异常;这在实践中可能很少见


这可能不如Valgrind好,因为它更像是一个调试器和手动检查,而不是一个get-a-nice-summary-at-end,但它可以工作。

要诊断溢出,可以使用浮点异常。例如,见。请注意,您可能需要使用特定于实现的函数来配置浮点错误的行为

注意,即使它们通常被称为“异常”,浮点错误不会引起C++异常。 cppreference代码显示了基于ieee754的实现的默认行为:您可以随时检查浮点异常标志。在输入计算之前,应清除标志。您可能希望等到计算完成后再查看它是否设置了任何标志,或者您可能希望检查您怀疑容易出错的每个操作

可能会有一些特定于实现的扩展,让这些“异常”触发一些您不能忽略的东西。在Windows /MSVC++中,Linux上可能是一个结构化异常(不是真正的C++),它可能是Sigfpe(因此,您需要一个信号处理程序来处理错误)。您需要实现特定的库函数,甚至编译器/链接器标志来启用这种行为

我仍然认为溢出不太可能是你的问题。如果某些输入变大,而其他值变小,则在组合它们时可能会失去精度。控制这种情况的一种方法是使用区间算法。有各种各样的库,包括


免责声明:我没有使用此库(或其他区间算术库)的经验,但也许这可以帮助您开始。

可能您需要调试一个算法的实现,其中您可能犯了编码错误,并希望跟踪正在执行的浮点计算。也许你需要一个钩子来检查所有被操作的值,寻找那些超出你期望范围的值。在C++中,您可以定义自己的代码<浮点< /Cord>类,并使用运算符重载以自然方式编写计算,同时保留检查所有计算的能力。 例如,这里有一个程序,它定义了一个
FP
类,并打印出所有的加法和乘法

#include <iostream>
struct FP {
    double value;
    FP( double value ) : value(value) {}
};
std::ostream & operator<< ( std::ostream &o, const FP &x ) { o << x.value; return o; }
FP operator+( const FP & lhs, const FP & rhs ) {
    FP sum( lhs.value + rhs.value );
    std::cout << "lhs=" << lhs.value << " rhs=" << rhs.value << " sum=" << sum << std::endl;
    return sum;
}
FP operator*( const FP & lhs, const FP & rhs ) {
    FP product( lhs.value * rhs.value );
    std::cout << "lhs=" << lhs.value << " rhs=" << rhs.value << " product=" << product << std::endl;
    return product;
}

int main() {
    FP x = 2.0;
    FP y = 3.0;
    std::cout << "answer=" << x + 2 * y << std::endl;
    return 0;
}
更新:我增强了程序(在x86上)以在每次浮点操作后显示浮点状态标志(仅实现加法和乘法,其他可以轻松添加)


除了已经发布的优秀建议之外,这里还有另一种方法。编写一个函数来检查浮点数据结构,进行范围和一致性检查。在主循环中插入对它的调用。为了检查其他变量,可以在检查程序发现问题后在检查程序中设置断点


与启用异常相比,这是更多的设置工作,但可以发现一些更微妙的问题,如不一致性和数量超出预期,而不会无限大,从而导致检测接近原始问题。

@LihO如果可以做到这一点,那么我也可以自己回答我的问题。一旦我收集了一些建议,我会很高兴地描述一下对我有用的东西。现在,我不知道从哪里开始寻找,或者这样的工具是否存在。整数还是浮点算术?在双精度浮点中不太可能出现溢出情况,因为当您超过大约308位十进制数字时(当您的数字太大而无法在指数中表示时),就会出现溢出情况。很可能您正在处理精度损失问题。要回答您的问题,我们需要了解您所说的“数值结果对于大输入大小变得不正确”@amdn浮点算法是什么意思。更具体地说,我计算大型网络。我还可以向您展示具体的,但我也对一般性的建议感兴趣。如果是关于浮点溢出的,那么这本书对您来说可能是一本有趣的读物:Linux与我相关。我想启用所有这些异常:
除零
溢出
下溢
不精确
无效
。我不熟悉这种C函数。我是否在我的
main
中简单调用
feenableexcept(?)
?正确的论点是什么?@cls-我添加了一些例子。谢谢。我
#包括
,然后是
std::feenableexcept(所有除外),但我得到
错误:“feenableexcept”不是“std”的成员。
。注意,
std::feclearException(FE\u ALL\u EXCEPT)没有错误
lhs=2 rhs=3 product=6
lhs=2 rhs=6 sum=8
answer=8
#include <iostream>

struct MXCSR {
    unsigned value;
    enum Flags {
        IE  = 0, // Invalid Operation Flag
        DE  = 1, // Denormal Flag
        ZE  = 2, // Divide By Zero Flag
        OE  = 3, // Overflow Flag
        UE  = 4, // Underflow Flag
        PE  = 5, // Precision Flag
    };
};
std::ostream & operator<< ( std::ostream &o, const MXCSR &x ) {
    if (x.value & (1<<MXCSR::IE)) o << " Invalid";
    if (x.value & (1<<MXCSR::DE)) o << " Denormal";
    if (x.value & (1<<MXCSR::ZE)) o << " Divide-by-Zero";
    if (x.value & (1<<MXCSR::OE)) o << " Overflow";
    if (x.value & (1<<MXCSR::UE)) o << " Underflow";
    if (x.value & (1<<MXCSR::PE)) o << " Precision";
    return o;
}

struct FP {
    double value;
    FP( double value ) : value(value) {}
};
std::ostream & operator<< ( std::ostream &o, const FP &x ) { o << x.value; return o; }
FP operator+( const FP & lhs, const FP & rhs ) {
    FP sum( lhs.value );
    MXCSR mxcsr, new_mxcsr;
    asm ( "movsd %0, %%xmm0 \n\t"
          "addsd %3, %%xmm0 \n\t"
          "movsd %%xmm0, %0 \n\t"
          "stmxcsr %1 \n\t"
          "stmxcsr %2 \n\t"
          "andl  $0xffffffc0,%2 \n\t"
          "ldmxcsr %2 \n\t"
          : "=m" (sum.value), "=m" (mxcsr.value), "=m" (new_mxcsr.value)
          : "m" (rhs.value)
          : "xmm0", "cc" );

    std::cout << "lhs=" << lhs.value
              << " rhs=" << rhs.value
              << " sum=" << sum
              << mxcsr
              << std::endl;
    return sum;
}
FP operator*( const FP & lhs, const FP & rhs ) {
    FP product( lhs.value );
    MXCSR mxcsr, new_mxcsr;
    asm ( "movsd %0, %%xmm0 \n\t"
          "mulsd %3, %%xmm0 \n\t"
          "movsd %%xmm0, %0 \n\t"
          "stmxcsr %1 \n\t"
          "stmxcsr %2 \n\t"
          "andl  $0xffffffc0,%2 \n\t"
          "ldmxcsr %2 \n\t"
          : "=m" (product.value), "=m" (mxcsr.value), "=m" (new_mxcsr.value)
          : "m" (rhs.value)
          : "xmm0", "cc" );

    std::cout << "lhs=" << lhs.value
              << " rhs=" << rhs.value
              << " product=" << product
              << mxcsr
              << std::endl;
    return product;
}

int main() {
    FP x = 2.0;
    FP y = 3.9;
    std::cout << "answer=" << x + 2.1 * y << std::endl;
    std::cout << "answer=" << x + 2 * x << std::endl;
    FP z = 1;
    for( int i=0; i<310; ++i) {
        std::cout << "i=" << i << " z=" << z << std::endl;
        z = 10 * z;
    }

    return 0;
}
lhs=10 rhs=1e+305 product=1e+306 Precision
i=306 z=1e+306
lhs=10 rhs=1e+306 product=1e+307
i=307 z=1e+307
lhs=10 rhs=1e+307 product=1e+308 Precision
i=308 z=1e+308
lhs=10 rhs=1e+308 product=inf Overflow Precision
i=309 z=inf
lhs=10 rhs=inf product=inf