简体   繁体   English

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

[英]How to detect double precision floating point overflow and underflow?

I have following variables: 我有以下变量:

double dblVar1;
double dblVar2;

They may have big values but less than double max. 它们可能具有较大的值但不到最大值的double

I have various arithmetic on above variables like addition, multiplication and power: 我对上面的变量有加法,乘法和幂等各种算法:

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

In all above I have to check overflow and underflow. 在上面所有我必须检查溢出和下溢。 How can I achieve this in C++? 我怎样才能在C ++中实现这一目标?

A lot depends on context. 很大程度上取决于背景。 To be perfectly portable, you have to check before the operation, eg (for addition): 为了完全便携,您必须在操作之前进行检查,例如(添加):

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

Similar logic can be used for the four basic operators. 类似的逻辑可以用于四个基本运算符。

If all of the machines you target support IEEE (which is probably the case if you don't have to consider mainframes), you can just do the operations, then use isfinite or isinf on the results. 如果您定位的所有计算机都支持IEEE(如果您不必考虑大型机,可能就是这种情况),您可以执行操作,然后对结果使用isfiniteisinf

For underflow, the first question is whether a gradual underflow counts as underflow or not. 对于下溢,第一个问题是逐渐下溢是否算作下溢。 If not, then simply checking if the results are zero and a != -b would do the trick. 如果没有,那么只需检查结果是否为零,并且a != -b就可以了。 If you want to detect gradual underflow (which is probably only present if you have IEEE), then you can use isnormal —this will return false if the results correspond to gradual underflow. 如果你想检测逐渐下溢(这可能只在你有IEEE时出现),那么你可以使用isnormal - 如果结果对应逐渐下溢,这将返回false。 (Unlike overflow, you test for underflow after the operation.) (与溢出不同,您在操作测试下溢。)

POSIX, C99, C++11 have <fenv.h> (and <cfenv> for C++11) which have functions to test the IEEE754 exceptions flags (which have nothing to do with C++ exceptions, it would be too easy): POSIX,C99,C ++ 11有<fenv.h> (以及C ++ 11的<cfenv> ),它具有测试IEEE754异常标志的功能(与C ++异常无关,它太容易了) :

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

The flag is a bitfield with the following bits defined: 该标志是一个位域,定义了以下位:

FE_DIVBYZERO
FE_INEXACT
FE_INVALID
FE_OVERFLOW
FE_UNDERFLOW

So you can clear them before the operations and then test them after. 所以你可以在操作之前清除它们,然后再测试它们。 You'll have to check the documentation for the effect of library functions on them. 您必须检查文档以了解库函数对它们的影响。

ISO C99 defines functions to query and manipulate the floating-point status word. ISO C99定义了查询和操作浮点状态字的函数。 You can use these functions to check for untrapped exceptions when it's convenient, rather than worrying about them in the middle of a calculation. 您可以使用这些函数在方便时检查未捕获的异常,而不是在计算过程中担心它们。

It provides 它提供

FE_INEXACT
FE_DIVBYZERO
FE_UNDERFLOW
FE_OVERFLOW
FE_INVALID

For example 例如

   {
       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 http://www.gnu.org/software/libc/manual/html_node/Status-bit-operations.html

With a decent compiler (which supports the newest C++ standard), you can use these functions: 使用合适的编译器(支持最新的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
 */

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM