I have three dedicated functions, each containing a __try/_except
block, and in which a EXCEPTION_ACCESS_VIOLATION
, EXCEPTION_INT_DIVIDE_BY_ZERO
, and EXCEPTION_FLT_DIVIDE_BY_ZERO
exception should be thrown, respectively. A filter is also provided for each __except
.
EXCEPTION_ACCESS_VIOLATION
and EXCEPTION_INT_DIVIDE_BY_ZERO
exceptions are thrown and caught successfully.
EXCEPTION_FLT_DIVIDE_BY_ZERO
exception is not thrown. Why?
How can I fix the code to catch the exception?
#include <Windows.h>
#include <iostream>
//------------------------------------------------------------------------------
int filter_access_violation(unsigned int code, EXCEPTION_POINTERS* xp)
{
if( code == EXCEPTION_ACCESS_VIOLATION )
{
std::cout << "OK: filter EXCEPTION_ACCESS_VIOLATIONn";
return EXCEPTION_EXECUTE_HANDLER;
}
std::cout << "Error: filter EXCEPTION_ACCESS_VIOLATIONn";
return EXCEPTION_CONTINUE_SEARCH;
}
void access_violation(int* pt)
{
std::cout << "nTesting: EXCEPTION_ACCESS_VIOLATIONn";
__try
{
*pt = 1;
std::cout << "Error: EXCEPTION_ACCESS_VIOLATION not thrownn";
}
__except (filter_access_violation(GetExceptionCode(), GetExceptionInformation()))
{
std::cout << "OK: __except EXCEPTION_ACCESS_VIOLATIONn";
}
}
//------------------------------------------------------------------------------
int filter_int_divide_by_zero(unsigned int code, EXCEPTION_POINTERS* xp)
{
if (code == EXCEPTION_INT_DIVIDE_BY_ZERO)
{
std::cout << "OK: filter EXCEPTION_INT_DIVIDE_BY_ZEROn";
return EXCEPTION_EXECUTE_HANDLER;
}
std::cout << "Error: filter EXCEPTION_INT_DIVIDE_BY_ZEROn";
return EXCEPTION_CONTINUE_SEARCH;
}
int int_divide_by_zero(int divisor)
{
std::cout << "nTesting: EXCEPTION_INT_DIVIDE_BY_ZEROn";
auto r = 1;
__try
{
r = 1 / divisor;
std::cout << "Error: EXCEPTION_INT_DIVIDE_BY_ZERO not thrownn";
}
__except (filter_int_divide_by_zero(GetExceptionCode(), GetExceptionInformation()))
{
std::cout << "OK: __except EXCEPTION_INT_DIVIDE_BY_ZEROn";
}
return r;
}
//------------------------------------------------------------------------------
int filter_float_divide_by_zero(unsigned int code, EXCEPTION_POINTERS* xp)
{
if (code == EXCEPTION_FLT_DIVIDE_BY_ZERO)
{
std::cout << "OK: filter EXCEPTION_FLT_DIVIDE_BY_ZEROn";
return EXCEPTION_EXECUTE_HANDLER;
}
std::cout << "Error: filter EXCEPTION_FLT_DIVIDE_BY_ZEROn";
return EXCEPTION_CONTINUE_SEARCH;
}
float float_divide_by_zero(float divisor)
{
std::cout << "nTesting: EXCEPTION_FLT_DIVIDE_BY_ZEROn";
auto r = 1.0f;
__try
{
r = 1.0f / divisor;
std::cout << "Error: EXCEPTION_FLT_DIVIDE_BY_ZERO not thrownn";
}
__except( filter_float_divide_by_zero(GetExceptionCode(), GetExceptionInformation()) )
{
std::cout << "OK: __except EXCEPTION_FLT_DIVIDE_BY_ZEROn";
}
return r;
}
int main()
{
access_violation(nullptr);
int_divide_by_zero(0);
float_divide_by_zero(0);
}
Output:
Testing: EXCEPTION_ACCESS_VIOLATION
OK: filter EXCEPTION_ACCESS_VIOLATION
OK: __except EXCEPTION_ACCESS_VIOLATION
Testing: EXCEPTION_INT_DIVIDE_BY_ZERO
OK: filter EXCEPTION_INT_DIVIDE_BY_ZERO
OK: __except EXCEPTION_INT_DIVIDE_BY_ZERO
Testing: EXCEPTION_FLT_DIVIDE_BY_ZERO
Error: EXCEPTION_FLT_DIVIDE_BY_ZERO not thrown
5
By default, for a C or C++ program compiled by MSVC and run on Windows,
floating-point division by zero does not raise an OS exception (nor a
language-level exception in C++).
In order to cause floating-point division by zero to raise an OS
exception, call the MSVC-specific
_controlfp
function like this:
_controlfp(0 /*new value*/, _EM_ZERODIVIDE /*mask of bits to change*/);
The floating point behavior is controlled by a 32-bit control word,
which can be changed using _controlfp
. The call above sets the
_EM_ZERODIVIDE
flag to zero.
The exception mask (EM) is a portion of the control word that specifies
which operations raise an exception. When a bit is set, that condition
is “masked”, meaning an exception is not raised. Clearing the bit
enables the corresponding exception.
Note the two different meanings of the word “mask” here: one is a
portion of the floating point control word saying what not to raise,
while the other is an element of the _controlfp
API saying what to
change.
If the above line of code is inserted as the first line of main()
in
the code in the question, then the output of the modified program is:
Testing: EXCEPTION_ACCESS_VIOLATION
OK: filter EXCEPTION_ACCESS_VIOLATION
OK: __except EXCEPTION_ACCESS_VIOLATION
Testing: EXCEPTION_INT_DIVIDE_BY_ZERO
OK: filter EXCEPTION_INT_DIVIDE_BY_ZERO
OK: __except EXCEPTION_INT_DIVIDE_BY_ZERO
Testing: EXCEPTION_FLT_DIVIDE_BY_ZERO
OK: filter EXCEPTION_FLT_DIVIDE_BY_ZERO
OK: __except EXCEPTION_FLT_DIVIDE_BY_ZERO <--- what we wanted
For completeness, I’ll note that the documentation linked above says one
should include <float.h>
, although including <windows.h>
appears
sufficient.
The documentation also says to add:
#pragma fenv_access (on)
but, in my testing, I did not observe any difference between omitting it
and including it.
Portability considerations
The equivalent of _controlfp
for GCC on most platforms is
feenableexcept
.
However, when compiling with MinGW, use _controlfp
as shown above.
Unfortunately, the
C99 standard floating-point environment API
declared in <fenv.h>
does not include any way to influence which
operations cause an OS trap.
1