c++nansigfpe

NAN -> distinguish between a division by zero and an exponent with a very large negative value


I included a check for nan's in my testsuite.

Now a SIGFPE is thrown at the following line

const double retVal =exp(exponent); 

The exponent has a value of approximately value -4000. This is of course very near to zero and without the nan check the calculation goes on with zero.

The signal SIGFPE is now been thrown. I assume that this is a denormal number (represented as NAN).

How can I distinguish between a division by zero and an exponent with a very large negative value?

In the second case, doing further calculations with zero works in my case, since I do not need to distinguish between +0 and -0.

EDIT: I tried what @user2079303 suggested. My code looks like this

#include <signal.h>
void fpe_handler(int signo, siginfo_t *info, void *context) {
    switch(info->si_code) {
        case FPE_INTDIV: // division by zero
            break;
        case FPE_FLTUND: // underflow
            break;
        // other cases ...
    }
}

int main() {
    struct sigaction action = {};
    action.sa_flags     = SA_SIGINFO;
    action.sa_sigaction = fpe_handler;
    sigaction(SIGFPE, &action, NULL);
    const double a = 0.0/0.0;
    if (a>0) 
        return 0;
    else
        return 1;
}

This compiles, but does not throw a signal or an error message.


Solution

  • Technically, from C++ perspective, division by zero has undefined behaviour, so there is no standard way to handle it.


    In the ubiquitous IEEE 754 standard division by zero is well defined, and will not raise a signal. It is possible to test whether a previously performed calculation divided by zero using the <cfenv> header:

    std::feclearexcept(FE_ALL_EXCEPT);
    // do calculation here
    if(std::fetestexcept(FE_DIVBYZERO)) {
        // I was just divided by zero. The sky is falling.
        std::abort();
    } else if(std::fetestexcept(FE_UNDERFLOW)) {
        // I just underflowed. This is my life now.
    }
    

    Now, dividing a normal floating point number with zero does not raise a signal unless you ask it to using feenableexcept (this is a GNU extension; non-standard).

    There is no way to distinguish different reasons for SIGFPE in standard C++. But there is a way in POSIX standard:

    void fpe_handler(int signo, siginfo_t *info, void *context)
    {
        switch(info->si_code) {
            case FPE_FLTDIV: // division by zero
                break;
            case FPE_FLTUND: // underflow
                break;
            // other cases ...
        }
    }
    
    // registering
    struct sigaction action = {};
    action.sa_flags     = SA_SIGINFO;
    action.sa_sigaction = fpe_handler;
    sigaction(SIGFPE, &action, NULL);