Fabian Knorr
Fabian Knorr

Reputation: 3184

Set flag for SIGFPE and continue execution ignoring FPEs

In a numerical application, I'd like to know if a floating point exception occured after the computation finishes. By default, floating point divisions and invalid operations are ignored silently.

My attempt is to enable FPEs I care about, handle SIGFPE by setting a flag and disabling them again to allow execution to continue:

#include <fenv.h>
#include <signal.h>
#include <stdio.h>

int caught = 0;
struct sigaction old_sa;
/* (2) */ fenv_t fenv_hold;

void sighandler()
{
    caught = 1;
    printf("Caught in handler, disabling\n");
    /* (1) */ fedisableexcept(FE_ALL_EXCEPT);
    /* (2) */ feholdexcept(&fenv_hold);
    sigaction(SIGFPE, &old_sa, NULL);
}

int main(void)
{
    struct sigaction sa;
    volatile double a=1, b=0;

    sigemptyset(&sa.sa_mask);
    sa.sa_flags = SA_SIGINFO; 
    sa.sa_sigaction = sighandler;
    sigaction(SIGFPE, &sa, &old_sa);

    feenableexcept(FE_DIVBYZERO);
    printf("Dividing by zero..\n");
    a/=b;
    printf("Continuing\n");
}

I took two approaches, the first one labeled with (1), the second one with (2). Neither of them works as expected.

Output:

Dividing by zero..
Caught in handler, disabling
Floating point exception (core dumped)

Expected Output:

Dividing by zero..
Caught in handler, disabling
Continuing

Upvotes: 3

Views: 1398

Answers (1)

Eric Postpischil
Eric Postpischil

Reputation: 222923

If you just want to know, after a computation finishes, whether a floating-point exception occurred, then you should not use signals, as they have a high overhead. Instead, use the floating-point exception flags, which are set quickly by the processor during normal execution. (However, accessing them may have some performance effect.)

See the C standard on <fenv.h>. Briefly:

  • Insert #include <fenv.h> in your source file.
  • Insert #pragma STDC FENV_ACCESS on before any source code that might access the floating-point flags or run under non-default floating-point modes.
  • If desired, insert #pragma STDC FENV_ACCESS off after the above source code, when the following source code does not access the flags or run under non-default modes.
  • Before a computation, execute feclearexcept(FE_ALL_EXCEPT) to clear flags.
  • After a computation, execute fetestexcept(exceptions) to test flags. exceptions should be a bitwise OR of FE_DIVBYZERO, FE_INEXACT, FE_INVALID, FE_OVERFLOW, and/or FE_UNDERFLOW, and possibly additional implementation-defined flags.

Note that some C implementations have poor support for accessing the floating-point environment.

Upvotes: 7

Related Questions