26

I have following variables:

double dblVar1;
double dblVar2;

They may have big values but less than double max.

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++?

ulidtko
  • 14,740
  • 10
  • 56
  • 88
venkysmarty
  • 11,099
  • 25
  • 101
  • 184

4 Answers4

19

A lot depends on context. To be perfectly portable, you have to check before the operation, e.g. (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.

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. 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. (Unlike overflow, you test for underflow after the operation.)

James Kanze
  • 150,581
  • 18
  • 184
  • 329
  • @ James what if a >0.0 and b >0.0 how do we check for overflow here – venkysmarty Mar 27 '13 at 10:27
  • @venkysmarty With the expresion I posted. It works for all cases (for addition). – James Kanze Mar 27 '13 at 10:44
  • `isnormal(5.0 - 5.0)` is false, making it appear like there is an underflow in the subtraction. I am unsure whether the OP wants this subtraction pointed out as underflow (actually, I am unsure what the OP means by “checking [for] underflow”). – Pascal Cuoq Mar 27 '13 at 16:12
  • @PascalCuoq That seems strange to me. (And I'm also unsure what exactly he's trying to do with regards to underflow.) – James Kanze Mar 27 '13 at 16:15
  • 1
    Floating-point underflow does not occur due to subtraction. But it can occur, for example, due to multiplication by a number close to zero or division by a number far from zero. – David K Apr 24 '14 at 22:06
11

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):

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.

AProgrammer
  • 51,233
  • 8
  • 91
  • 143
  • The macros specifying the bit fields are only conditionally supported; not all systems will have them. (And I can't find in the standard where it specifies the meaning of `FF_UNDERFLOW`. In IEEE, does it include gradual underflow or not?) – James Kanze Mar 27 '13 at 09:56
  • @JamesKanze IEEE-754 does support unnormalized values (that is, gradual underflow). – Alexey Frunze Mar 27 '13 at 10:01
  • @JamesKanze, it is defined in IEEE 754. From memory, it allows the implementation to choose between several semantics, but ISTR that for all of them FE_UNDERFLOW is raised when generating denormals if the result is not exact (if I'm not mistaken, the lenience is about detecting the situation before or after rounding). – AProgrammer Mar 27 '13 at 10:07
  • @AProgrammer Yes. And `FF_UNDERFLOW` is defined as simply reflecting whatever the hardward does ("exception" in the sense used in the IEEE standard), so you should get it when gradual underflow occurs. (Whether this is what is wanted or not is another question.) – James Kanze Mar 27 '13 at 10:43
  • @AProgrammer - just a little quibble: the latest version of IEEE 754 has replaced "denormal" with "subnormal". – Pete Becker Mar 27 '13 at 11:24
9

With a decent compiler (which supports the newest C++ standard), you can use these functions:

#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
 */
ulidtko
  • 14,740
  • 10
  • 56
  • 88
  • @ ulidtko above code doesnot compile on VS2008 as I am getting cannot open cfenv file error. How can I overcome this situation. Thanks – venkysmarty Mar 27 '13 at 10:04
7

ISO C99 defines functions to query and manipulate the floating-point status word. 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

Saqlain
  • 17,490
  • 4
  • 27
  • 33