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.
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.
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.
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.
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.
If you are are building a calculator interface and want to process the floating point exceptions, you should :
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
:
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With