Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

IEEE-754 Floating-point Exceptions in C

I am writing a floating-point calculator-interface, in C, that allows the mathematical functions defined in math.h to be accessed at runtime. The interface is implemented as a function that behaves like strtold(). It is based on ASCII and is supposed to be as portable as ASCII but in order for that to be true I need to handle floating-points in as portable a way as possible. I am happy with limiting support to IEEE-754 floating-points but I am not sure how to handle the exceptions defined by IEEE-754 (overflow, underflow, etc.). First of all I am pretty sure the only way to check for exceptions that will work in all rounding modes is checking the status flags themselves; in order to do that I will need fenv.h (defined in Annex F of C99) so I want to know how portable fenv.h is in practice. I also do not completely understand how fenv.h is supposed to work; it appears to me that the status flags are centralized but for whatever reason I was under the impression each floating-point had the flags built-in. Also I know C99 says functions defined in math.h may overflow and underflow but I do not understand how I am supposed to check for these exceptions. So to summarize I am looking for an example of how to use fenv.h to check for an overflow caused by multiplication and an explanation of how to properly error check the functions defined in math.h.

like image 834
John Vulconshinz Avatar asked May 12 '15 04:05

John Vulconshinz


People also ask

What is a floating point exception in C?

A floating point exception is an error that occurs when you try to do something impossible with a floating point number, such as divide by zero.

What is floating point exception Sigfpe?

When a floating-point exception raises the SIGFPE signal, the process terminates and produces a core file if no signal-handler subroutine is present in the process. Otherwise, the process calls the signal-handler subroutine. Floating-point exception subroutines.

What is floating point exception core dumped?

Floating point exception (core dumped) is an error that arises when your application tries to do something that is not allowed with a floating point number.


2 Answers

In theory, the following function multiplies two numbers, returning true if an overflow occurred and false if not:

bool mul(double &a, double b) {
  feclearexcept(FE_OVERFLOW);
  a *= b;
  return fetestexcept(FE_OVERFLOW) != 0;
}

The standard states that you need to #pragma FENV_ACCESS ON in order to use this. However, I have yet to use a compiler that cares about that pragma or a compiler that knows that floating-point multiplication has a side effect reflected in the exception flags---both gcc and clang will happily "optimise away" a "dead" floating-point operation. gcc bug 34678 concerns this behaviour and I imagine there's a similar bug against clang. This caveat also applies to any use of rounding modes other than round-to-nearest-breaking-ties-to-even in a program.

like image 92
tmyklebu Avatar answered Sep 28 '22 15:09

tmyklebu


If you are are building a calculator interface and want to process the floating point exceptions, you should :

  • reset flags before any floating point operation
  • test flags after the operation

You will have no help for it so you must implement if by hand. Example :

double mul(double a, double b, int *status) {
    #pragma STDC FENV_ACCESS ON
    fexcept_t flags;
    int sv_status, f_status = -1;
    double resul;

    sv_status = fegetexceptflag(&flags, FE_ALL_EXCEPT) != 0; /* save flags */
    if (sv_status == 0) {
        f_status = feclearexcept(FE_ALL_EXCEPT);   /* clear all fp exception con
ditions */
    }
    resul = a * b;
    if (f_status == 0) {
        *status = fetestexcept(FE_ALL_EXCEPT); /* note conditions */
    }
    if (sv_status == 0) {
        fesetexceptflag(&flags, FE_ALL_EXCEPT); /* restore initial flags */
    }
    return resul;

}

Demo :

int main ()
{
        double d2, d3, d4;
        int status;
        double d = 1e100;

        feraiseexcept(FE_OVERFLOW | FE_INEXACT);
        status = fetestexcept(FE_ALL_EXCEPT);
        printf("initial status : %x\n", status);
        d2 = mul(3., 4., &status);
        printf("resul 3 * 4 : %g - status %x (%x)\n", d2,
                status, fetestexcept(FE_ALL_EXCEPT));
        d2 = mul(d, d, &status);
        printf("resul d * d : %g - status %x (%x)\n", d2,
                status, fetestexcept(FE_ALL_EXCEPT));
        d2 = mul(d2, d, &status);
        printf("resul d *d *d : %g - status %x (%x)\n", d2,
                status, fetestexcept(FE_ALL_EXCEPT));
        d2 = mul(d2, d, &status);
        printf("resul d *d *d*d : %g - status %x (%x)\n", d2,
                status, fetestexcept(FE_ALL_EXCEPT));
        d2 = mul(d2, d, &status);
        printf("resul d *d *d*d*d : %g - status %x (%x)\n", d2,
                status, fetestexcept(FE_ALL_EXCEPT));
        return 0;
}

gives :

initial status : 28
resul 3 * 4 : 12 - status 0 (28)
resul d * d : 1e+200 - status 20 (28)
resul d *d *d : 1e+300 - status 20 (28)
resul d *d *d*d : inf - status 28 (28)
resul d *d *d*d*d : inf - status 0 (28)

That means that mul :

  • correctly sets the status flag
  • keeps the floating point exception flag unchanged

As this code only uses macros and functions defined in C specification it should work on any C99 compliant compiler.

Now it is up to you to actually process the flag.

References : ISO/IEC 9899:201x (ISO C11) Committee Draft

Nota : Clang (at least) issues a warning saying the it ignores pragma STDS FENV_ACCESS but it works fine

like image 40
Serge Ballesta Avatar answered Sep 28 '22 16:09

Serge Ballesta