3

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
Fabian Knorr
  • 3,134
  • 3
  • 20
  • 32

1 Answers1

6

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.

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.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
  • 1
    If the implementation has poor support, using a signal handler won't work any better. The issue with support is that the compiler might make invalid optimizations that cause different exceptions to take place than should have happened on the abstract machine. – R.. GitHub STOP HELPING ICE Jul 10 '13 at 22:16