关于c ++:如何检测双精度浮点上溢和下溢?

How to detect double precision floating point overflow and underflow?

我有以下变量:

1
2
double dblVar1;
double dblVar2;

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

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

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

在以上所有内容中,我必须检查上溢和下溢。 如何在C ++中实现呢?


在很大程度上取决于上下文。要完全便携,您必须
操作前检查,例如(用于添加):

1
2
3
4
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。
(与溢出不同,您需要在操作后测试下溢。)


POSIX,C99,C ++ 11具有(对于C ++ 11为),它们具有测试IEEE754异常标志的功能(与C ++异常无关,这太容易了):

1
2
3
4
5
int  feclearexcept(int);
int  fegetexceptflag(fexcept_t *, int);
int  feraiseexcept(int);
int  fesetexceptflag(const fexcept_t *, int);
int  fetestexcept(int);

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

1
2
3
4
5
FE_DIVBYZERO
FE_INEXACT
FE_INVALID
FE_OVERFLOW
FE_UNDERFLOW

因此,您可以在操作之前清除它们,然后在进行测试。您必须检查文档,了解库函数对其的影响。


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

它提供

1
2
3
4
5
FE_INEXACT
FE_DIVBYZERO
FE_UNDERFLOW
FE_OVERFLOW
FE_INVALID

例如

1
2
3
4
5
6
7
8
9
10
   {
       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


通过一个不错的编译器(它支持最新的C ++标准),您可以使用以下功能:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#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
 */