如何检测双精度浮点溢出和下溢?

时间:2013-03-27 09:06:27

标签: c++ floating-point

我有以下变量:

double dblVar1;
double dblVar2;

它们可能具有较大的值但小于double最大值

我对上述变量有各种算法,如加法,乘法和幂:

double dblVar3 = dblVar1 * dblVar2; 
double dblVar4 = dblVar1 + dblVar2;
double dblVar5 = pow(dblVar1, 2);

总之,我必须检查溢出和下溢。我怎样才能在C ++中实现这一目标?

4 个答案:

答案 0 :(得分:12)

很大程度上取决于背景。为了完全便携,你必须 在操作之前检查,例如(补充):

if ( (a < 0.0) == (b < 0.0)
    && std::abs( b ) > std::numeric_limits<double>::max() - std::abs( a ) ) {
    //  Addition would overflow...
}

类似的逻辑可以用于四个基本运算符。

如果您定位的所有计算机都支持IEEE(即 如果您不必考虑大型机,可能就是这种情况),你 可以执行操作,然后使用isfiniteisinf 结果。

对于下溢,第一个问题是是否逐渐下溢 算作是否下溢。如果没有,那么只需检查是否 结果为零,a != -b可以解决问题。如果你想 检测逐渐下溢(这可能只是存在 你有IEEE),那么你可以使用isnormal - 这样就可以了 如果结果对应于逐渐下溢,则返回false。 (与溢出不同,您在操作后测试下溢。)

答案 1 :(得分:9)

POSIX,C99,C ++ 11有<fenv.h>(和<cfenv>用于C ++ 11),它具有测试IEEE754异常标志的功能(与C ++异常无关,它会太容易了):

int  feclearexcept(int);
int  fegetexceptflag(fexcept_t *, int);
int  feraiseexcept(int);
int  fesetexceptflag(const fexcept_t *, int);
int  fetestexcept(int);

该标志是一个位域,定义了以下位:

FE_DIVBYZERO
FE_INEXACT
FE_INVALID
FE_OVERFLOW
FE_UNDERFLOW

所以你可以在操作之前清除它们,然后再测试它们。您必须查看文档以了解库函数对它们的影响。

答案 2 :(得分:5)

ISO C99定义了查询和操作浮点状态字的函数。您可以使用这些函数在方便时检查未捕获的异常,而不是在计算过程中担心它们。

提供

FE_INEXACT
FE_DIVBYZERO
FE_UNDERFLOW
FE_OVERFLOW
FE_INVALID

例如

   {
       double f;
       int raised;
       feclearexcept (FE_ALL_EXCEPT);
       f = compute ();
       raised = fetestexcept (FE_OVERFLOW | FE_INVALID);
       if (raised & FE_OVERFLOW) { /* ... */ }
       if (raised & FE_INVALID) { /* ... */ }
       /* ... */
     }

http://www.gnu.org/software/libc/manual/html_node/Status-bit-operations.html

答案 3 :(得分:5)

使用合适的编译器(支持最新的C ++标准),您可以使用these函数:

#include <cfenv>
#include <iostream>

int main() {
    std::feclearexcept(FE_OVERFLOW);
    std::feclearexcept(FE_UNDERFLOW);

    double overflowing_var = 1000;
    double underflowing_var = 0.01;

    std::cout << "Overflow flag before: " << (bool)std::fetestexcept(FE_OVERFLOW) << std::endl;
    std::cout << "Underflow flag before: " << (bool)std::fetestexcept(FE_UNDERFLOW) << std::endl;

    for(int i = 0; i < 20; ++i) {
        overflowing_var *= overflowing_var;
        underflowing_var *= underflowing_var;
    }

    std::cout << "Overflow flag after: " << (bool)std::fetestexcept(FE_OVERFLOW) << std::endl;
    std::cout << "Underflow flag after: " << (bool)std::fetestexcept(FE_UNDERFLOW) << std::endl;
}

/** Output:
  Overflow flag before: 0
  Underflow flag before: 0
  Overflow flag after: 1
  Underflow flag after: 1
 */