5.3.4. C9X-compatible functions

In addition to the above functions, ARM also supports a set of functions defined in the C9X draft standard. These functions are the only interface that allows you to install custom exception trap handlers with the ability to invent a return value. All the functions, types, and macros in this section are defined in fenv.h.

C9X defines two data types, fenv_t and fexcept_t. The C9X draft standard does not define any details about these types, so for portable code you must treat them as opaque. ARM defines them to be structure types, for details see ARM extensions to the C9X interface.

The type fenv_t is defined to hold all the information about the current floating-point environment:

The type fexcept_t is defined to hold all the information relevant to a given set of exceptions.

C9X also defines a macro for each rounding mode and each exception. The macros are as follows:

FE_DIVBYZERO
FE_INEXACT
FE_INVALID
FE_OVERFLOW
FE_UNDERFLOW
FE_ALL_EXCEPT
FE_DOWNWARD
FE_TONEAREST
FE_TOWARDZERO
FE_UPWARD

The exception macros are bit fields. The macro FE_ALL_EXCEPT is the bitwise OR of all of them.

Handling exception flags

C9X provides three functions to clear, test and raise exceptions:

void feclearexcept(int excepts);
int fetestexcept(int excepts);
void feraiseexcept(int excepts);

The feclearexcept function clears the sticky flags for the given exceptions. The fetestexcept function returns the bitwise OR of the sticky flags for the given exceptions (so that if the Overflow flag was set but the Underflow flag was not, then calling fetestexcept(FE_OVERFLOW|FE_UNDERFLOW) would return FE_OVERFLOW).

The feraiseexcept function raises the given exceptions, in unspecified order. If an exception trap is enabled for an exception raised this way, it is called.

C9X also provides functions to save and restore everything about a given exception. This includes the sticky flag, whether the exception is trapped, and the address of the trap handler, if any. These functions are:

void fegetexceptflag(fexcept_t *flagp, int excepts);
void fesetexceptflag(const fexcept_t *flagp, int excepts);

The fegetexceptflag function copies all the information relating to the given exceptions into the fexcept_t variable provided. The fesetexceptflag function copies all the information relating to the given exceptions from the fexcept_t variable into the current floating-point environment.

Note

fesetexceptflag can be used to set the sticky flag of a trapped exception to 1 without calling the trap handler, whereas feraiseexcept calls the trap handler for any trapped exception.

Handling rounding modes

C9X provides two functions for controlling rounding modes:

int fegetround(void);
int fesetround(int round);

The fegetround function returns the current rounding mode, as one of the macros defined above. The fesetround function sets the current rounding mode to the value provided. fesetround returns zero for success, or nonzero if its argument is not a valid rounding mode.

Saving the whole environment

C9X provides functions to save and restore the entire floating-point environment:

void fegetenv(fenv_t *envp);
void fesetenv(const fenv_t *envp);

The fegetenv function stores the current state of the floating-point environment into the fenv_t variable provided. The fesetenv function restores the environment from the variable provided.

Like fesetexceptflag, fesetenv does not call trap handlers when it sets the sticky flags for trapped exceptions.

Temporarily disabling exceptions

C9X provides two functions that enable you to avoid risking exception traps when executing code that might cause exceptions. This is useful when, for example, trapped exceptions are using the ARM default behavior. The default is to cause SIGFPE and terminate the application.

int feholdexcept(fenv_t *envp);
void feupdateenv(const fenv_t *envp);

The feholdexcept function saves the current floating-point environment in the fenv_t variable provided, sets all exceptions to be untrapped, and clears all the exception sticky flags. You can then execute code that might cause unwanted exceptions, and make sure the sticky flags for those exceptions are cleared. Then you can call feupdateenv. This restores any exception traps and calls them if necessary.

For example, suppose you have a function frob() that might cause the Underflow or Invalid Operation exceptions (assuming both exceptions are trapped). You are not interested in Underflow, but you want to know if an invalid operation is attempted. So you could do this:

fenv_t env;
feholdexcept(&env);
frob();
feclearexcept(FE_UNDERFLOW);
feupdateenv(&env);

Then, if the frob() function raises Underflow, it is cleared again by feclearexcept, and so no trap occurs when feupdateenv is called. However, if frob() raises Invalid Operation, the sticky flag is set when feupdateenv is called, and so the trap handler is invoked.

This mechanism is provided by C9X because C9X specifies no way to change exception trapping for individual exceptions. A better method is to use __ieee_status to disable the Underflow trap while leaving the Invalid Operation trap enabled. This has the advantage that the Invalid Operation trap handler is provided with all the information about the invalid operation (which operation was being performed on what data), and can invent a result for the operation. Using the C9X method, the Invalid Operation trap handler is called after the fact, receives no information about the cause of the exception, and is called too late to provide a substitute result.

Copyright © 1999-2001 ARM Limited. All rights reserved.ARM DUI 0067D
Non-Confidential