Catching the Floating-point Exceptions
The
try-except statement is a Microsoft extension to the C and C++ languages that enables 32-bit target applications to gain control when exceptions occur. These exceptions call C, or structured or Win32 exceptions and are identified by an unsigned integer value. Also, they are referenced as "asynchronous." This means that they are secondary to the normal flow of control. According to Intel documentation, in the native mode (not MS DOS), the software exception handler is invoked immediately before the execution of the next floating-point instructionunless it is not one of the non-waiting instructions, the
WAIT\FWAIT instruction, or the next
MMX™ instruction. The following code snippet shows how to use C exception handling mechanism:
__try //The start of the guarded block.
{
//Code that may generate a floating-point exception
} //The end of the guarded block.
__except (EvaluateException(GetExceptionCode()))
{
//May evaluate globalVar here
//Code that does appropriate processing
}
//May evaluate globalVar here
When an exception occurs during the execution of the guarded block or any function called from it, the
_except expression is evaluated. Here,
GetExceptionCode() retrieves the code that identifies the type of occurred exception. The
EvaluateException() calls the filter expression and can be any function that returns an integer value. There are three predefined values that can be returned and that define the program execution pass. The values are defined in the
excpt.h file, and they are:
- EXCEPTION_EXECUTE_HANDLER: The except block is executed.
- EXCEPTION_CONTINUE_SEARCH: The exception handler search continues.
- EXCEPTION_CONTINUE_EXECUTION: Execution continues as the exception never occurred.
The following code shows an example of the filter expression for x86:
int EvaluateException(int exceptionCode)
{
//May evaluate exeptionCode
//May set a global variable that can be evaluated in
//or after _except guard code.
globalVar = _clear87(); //Get floating-point status word.
//The above function also sets the
//busy bit to 0 to show that FPU
//is not busy.
return EXCEPTION_EXECUTE_HANDLER;//force execution of
//the _except() guard code.
}
Instead of calling
EvaluateException(), you may call
_fpieee_flt(). This function is defined in the
fpieee.h file:
_fpieee_flt(GetExceptionCode(), GetExceptionInformation(),
MyFpieeeHandler)
You supply MyFpieeeHandler() according to its signature:
int MyFpieeeHandler( _FPIEEE_RECORD* );
The possible return values are the same as for
EvaluateException().
The following code shows an example of the filter expression for x86:
int EvaluateException(int exceptionCode)
{
unsigned long cntrReg = 0;
_asm
{
stmxcsr [cntrReg] //Get MXCSR register
and [cntrReg], 000000001h //Get invalid operation
//flag bit 0.
//bit 2 – divide-by-zero flag
//bit 3 - overflow flag
//bit 4 – underflow flag
}
if (cntReg > 0)
{
_asm
{
stmxcsr [cntrReg] //Get MXCSR register
and [cntrReg], 0FFFFFFFEh //Clear sticky
//invalid operation flag
ldmxcsr [cntrReg] //Load MXCSR register
}
globalVar = _SW_INVALID;
}
return EXCEPTION_EXECUTE_HANDLER;//force execution of
//the _except() guard code.
}
If a C exception is raised in a C++ program, it can be handled as described above. However, for C++, Microsoft recommends that you use the C++ try-catch exception handling mechanism. This mechanism is fully "synchronous," meaning that it only occurs when it is thrown. The difference between C structured and C++ typed exception handling is that the latter requires the use of the ellipsis handler in order to catch the floating-point exceptions in C++ code. The following code demonstrates this:
try
{
//Code that may generate a floating-point exception
}
catch(. . .)
{
int fpStatusWord = _clear87();
//You may analyze the bits of the status word.
//Code that does appropriate processing
}
| Author's Note: Use catch(. . .) with precaution; see the compilers option /EH below. |
It's also possible to associate the Win32 exceptions with specific wrapper classes, as shown below. Here's how to define a wrapper:
class SeWrapper
{
private:
unsigned int seCode;
SeWrapper() {}
public:
SeWrapper(unsigned int n) : (seCode(n) {}
~SeWrapper() {}
unsigned int GetSeCode() {return seCode;}
};
Define a native compiled function (compiled with /c) according to the following signature:
void SeTranslator(unsigned int n, struct _EXCEPTION_POINTER* ptr)
{
//This function should throw an exception of SeWrapper class.
throw SeWrapper(n);
}
Install the translator function that will be called by the internal exception-handling mechanism when a C exception is thrown:
_set_se_translator(SeTranslator);
Each thread should install its own translator function. Now, you can use C++ typed exception:
try
{
//Code that may generate a floating-point exception
}
catch(SeWrapper ex)
{
//You may analyze ex.
//Code that does appropriate processing
}
After you have your code in place, you need to select right project properties.