Applying C - Floating Point
Written by Harry Fairhead   
Tuesday, 21 May 2024
Article Index
Applying C - Floating Point
Floating Point Algorithms
Detecting Problems
Floating Point Reconsidered

Detecting Problems

You can see that IEEE floating point has been designed to carry on with the calculation and return a result as close to the correct result as is reasonable. However, in many cases this is not acceptable and any arithmetic irregularity needs to be handled. The bad news is that there is no portable way of doing this. Only C99 defines functions that allow you to check for problems in floating point and if you can’t use C99 then you have to fall back on compiler and machine specifics. While all floating point hardware has a status register that has flags for each type of floating point error, there is no standard way to access this except under C99.

However, there is also a GNU library that implements the C99 floating point functions and this can be used with GCC with any version of C. Of course, exactly what works depends on the hardware.

There are five types of exception defined in fenv.h:

FE_INEXACT inexact arithmetic

FE_DIVBYZERO divide by zero

FE_UNDERFLOW The underflow exception.

FE_OVERFLOW The overflow exception.

FE_INVALID The invalid exception.

These are used to create a bit mask by ORing them together to indicate what exceptions you are interested in.

There is also:

FE_ALL_EXCEPT

which is the bitwise OR of all of them.

The key to using the facility is a set of functions that will read, set and clear the floating point status register. The two most important are:

int feclearexcept (int excepts) - clear flags

int fetestexcept (int excepts) – tests flags for set

For example, to detect just a divide by zero exception:

feclearexcept(FE_ALL_EXCEPT);   
float w = (1 / 0.0);
int raised = fetestexcept(FE_DIVBYZERO);
if(raised) printf("divide by zero");

For this to work you have to include:

#include <fenv.h>

and you have to link against the GNU math library, which means you need to add libm.a to the libraries.

You can check for any error using:

feclearexcept(FE_ALL_EXCEPT);   
float w = 0.0/0.0;
if(fetestexcept(FE_DIVBYZERO))  printf(" FE_DIVBYZERO");
if(fetestexcept(FE_INEXACT))    printf(" FE_INEXACT");
if(fetestexcept(FE_INVALID))    printf(" FE_INVALID");
if(fetestexcept(FE_OVERFLOW))   printf(" FE_OVERFLOW");
if(fetestexcept(FE_UNDERFLOW))  printf(" FE_UNDERFLOW");
if(fetestexcept(FE_ALL_EXCEPT)==0) printf(" none");

For many small machines there is often no choice but to examine the hardware directly to determine the state of the floating point hardware.

It is also possible to raise a signal, using the feenableexcept function, when a floating point error occurs, but this is very dependent on operating system and hardware. For example, this program works under Linux on x86, but not under MinGW or ARM Linux:

#pragma STDC FENV_ACCESS on
#define _POSIX_C_SOURCE  200809L
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <math.h>
#include <float.h>
#include <fenv.h>
void signalHandler(int sig, siginfo_t *info, 
void *ucontext) { printf("Floating Point exception %d\n", sig); printf("%d\n", info->si_code); } int main(int argc, char** argv) { sigset_t mask; sigfillset(&mask); struct sigaction psa = {0}; psa.sa_mask = mask; psa.sa_sigaction = &signalHandler; psa.sa_flags = SA_SIGINFO; sigaction(SIGFPE, &psa, NULL); feenableexcept(FE_INVALID | FE_DIVBYZERO | FE_OVERFLOW | FE_UNDERFLOW); feenableexcept(FE_DIVBYZERO); printf("flags %d\n" , feenableexcept (FE_DIVBYZERO)); float w = 1.0 / 0.0; printf("%f \n", w); return (EXIT_SUCCESS); }

In chapter but not in this extract:

  • Mixed Arithmetic – Casting


Last Updated ( Tuesday, 21 May 2024 )