26

我有以下变量:

double dblVar1;
double dblVar2;

它们可能有很大的值,但小于double最大值。

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

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

综上所述,我必须检查上溢和下溢。如何在 C++ 中实现这一点?

4

4 回答 4

17

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

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

于 2013-03-27T09:36:00.307 回答
10

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

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

于 2013-03-27T09:43:39.970 回答
9

使用像样的编译器(支持最新的 C++ 标准),您可以使用以下函数:

#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
 */
于 2013-03-27T09:52:15.893 回答
7

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

于 2013-03-27T09:46:58.147 回答