== Repost the article of DQNOK (davidlqualls gmail.com)
== Posted at 2008/01/03 11:19 to c++.windows.32-bits
After two weeks of ripening in the windows32-bit forum, I thought
perhaps someone here might read and assist. Thanks in advance.
David
I can't figure out how to trap either floating point, or integer
divide by zero.
On some other compilers, I can trap SIGFPE and that does the trick
Hi,
The x87 FPU has a register called "x87 FPU Control Word".
Some bits in this register control whether exceptions should be
generated by conditions such as (floating point) precision issues,
underflow, overflow, division by 0, denormal/invalid operands.
This register can be controlled using the Assembler instructions
FLDCW, FSTCW/FNSTCW, FCLEX/FNCLEX.
The documentation can be found in Section 8
of "(Intel 64 and) IA-32 Software Developer's Manual"
(http://www.intel.com/products/processor/manuals/).
The default value for the x87 Control Word is:
0x1372 (Borland) -> division by 0 is signaled
0x027F (Visual) -> division by 0 is NOT signaled
0x137F (Digital Mars) -> division by 0 is NOT signaled
For greater portability (eg: Alpha CPU's) while using
Windows compilers, the _control87, _status87, _clear87 functions
and the EM_ZERODIVIDE (or _EM_ZERODIVIDE) and similar constants
from the header <cfloat> can be used.
After setting the control register so that division by 0
raises an exception, the next question is how to catch it.
Windows SEH (Structured Exception Handling) can be used
for this purpose (__try/__except/__finally).
This should not be confused with standard C++ exception handling
(try/catch).
A very good documentation for SEH is here:
http://www.jorgon.freeserve.co.uk/ExceptFrame.htm,
but one can also search the web for "structured exception handling"
and follow the links to the Microsoft documentation.
Here is an example program. I think you should modify it to use
_control87 etc. instead of inline assembler.
//-----------------------------------------------------------------
#if defined (__BORLANDC__)
#pragma inline
#endif
#include <iostream>
#include <iomanip>
//#include <cfloat>
#define NOMINMAX
#include <windows.h>
// The functions Double0 and Int0 attempt to prevent
// precomputing the result at compile-time
// (eg. Borland does that, even in some debug builds).
// In order to prevent these precomputations even in release builds,
// you should move these 2 functions to a separate .cpp file !
double Double0 (bool bReturnZero)
{
if (bReturnZero)
return 0.0;
else
return 1.0;
}
int Int0 (bool bReturnZero)
{
if (bReturnZero)
return 0;
else
return 1;
}
int main ()
{
WORD w;
__asm fstcw w;
std::cout
<< "FPU Control Word = "
<< std::hex << std::uppercase << std::setfill ('0') <<
std::setw (2) << w << "h\n";
if (w & 4)
{
std::cout << "Your environment masks division by 0 by default,
but we're about to change that now. ^^\n";
w &= ~4;
__asm fldcw w;
__asm fwait;
}
double d = 10.0;
__try
{
std::cout << "Performing floating-point division by 0; fasten
your seatbelts !\n" << std::flush;
d = 8.0 / Double0 (true);
std::cout << "This message should not be displayed !\n";
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
__asm fnclex;
std::cout << "Caught by Structured Exception Handling.\n";
}
std::cout << "d = " << d << "\n";
int i = 10;
__try
{
std::cout << "Performing integer division by 0. This is gonna
hurt !\n" << std::flush;
i = 8 / Int0 (true);
std::cout << "This message should not be displayed !\n";
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
__asm fnclex;
std::cout << "Caught by Structured Exception Handling.\n";
}
std::cout << "i = " << std::dec << i << "\n";
std::cout << "I hope you've enjoyed this example !\n";
return 0;
}
//-----------------------------------------------------------------
Thanks! Very nice.
I haven't tried any of this yet. I wonder if setting the
EM_ZERODIVIDE flag causes an INTEGER divide-by-zero to signal...
I don't plan to use structured exception handling: plan on using
standard C signals and longjmp instead. I just couldn't figure
how to convince the compilers to raise signals on divide by zero.
Thanks again.
DQNOK wrote:
== Repost the article of DQNOK (davidlqualls gmail.com)
== Posted at 2008/01/03 11:19 to c++.windows.32-bits
After two weeks of ripening in the windows32-bit forum, I thought
perhaps someone here might read and assist. Thanks in advance.
David
I�can't�figure�out�how�to�trap�either�floating�point,�or�integer
divide�by�zero.
On�some�other�compilers,�I�can�trap�SIGFPE�and�that�does�the�trick
for�both�floating�point,�and�for�integer�divide�by�zero.��On�dmc
and�microsoft�VC�(in�this�regard,�the�two�compilers�seem�to�act
identically),�the�expression:
�double�d�=�8.0/0.0;�//does�not�raise�a�signal�that�I�can�tell.
does�nothing�but�assign�inf�to�d�and�move�on.��However,�the
expression:
�int�i�=�8/0;
causes�the�program�to�abort.��I�can't�figure�out�how�to�trap�the
error�and�prevent�the�abort,�or�how�to�trap�the�floating�point
divide�by�zero.
David
C++ exceptions are not thrown for machine-level events like
divide-by-zero. It’s assumed these are dealt with by some other
mechanism, like the operating system or hardware. That way, C++
exceptions can be reasonably efficient, and their use is isolated to
program-level exceptional conditions.
Currently I am also looking for the solution. I'll take a look at
Windows SEH as suggested by Imbecil.
== Quote from Sunny Pal Singh (arorasp2000 hotmail.com)'s article
C++ exceptions are not thrown for machine-level events like
divide-by-zero. It's assumed these are dealt with by some other
mechanism, like the operating system or hardware. That way, C++
exceptions can be reasonably efficient, and their use is
isolated to
program-level exceptional conditions.
Currently I am also looking for the solution. I'll take a look at
Windows SEH as suggested by Imbecil.
That's good to know.
I was trying to get the machine (or OS) to raise a signal. I'm
using the classic C (NOT C++) signals and longjmp mechanisms.
While I haven't yet tried monkeying with the x87 control word,
this does seem to be the way to do it. It explains why Borland
raises the signal, but Visual and DMC do not.
David
This apear to be by design. You can divide by zero in "Floating-
Point Arithmetics".
In other words if huge calculations (sub-atomic to galaxies), you
might want to have access to "Infinity" as a value.
Your only choice here is to if( val == INF ) or something like
that.
Hi,
Good practice is to check for division by zero before it occurs.
The following code should enable floating point exceptions along
with a few other floating point issues (it works with vc++):
unsigned int u;
unsigned int control_word;
_controlfp_s(&control_word, 0, 0);
u = control_word & ~(_EM_INVALID | _EM_DENORMAL | _EM_ZERODIVIDE
| _EM_OVERFLOW | _EM_UNDERFLOW /*| _EM_INEXACT*/);
_controlfp_s( &control_word, u, _MCW_EM);
Use a try catch block to capture the exception.
Regards
Damian
== Quote from Damian (damian.dixon gmail.com)'s article
Hi,
Good practice is to check for division by zero before it occurs.
Not exactly sure what you have in mind here, but if you mean:
...
if( 0 == x ) throw( DivByZero );
//otherwise, press ahead with the division.
z = y/x;
...
then I contend that:
1) it bloats the code,
2) it interrupts the natural logical flow of the code, and
3) it slows execution by requireing a test before the div.
The couple of architectures I am familiar with have a hardware
trap (an unmaskable interrupt) that prevents the illegal operation
of division by zero. This doesn't take any extra processor
cycles -- the processor itself raises the error; not the
software. In a C environment, this (should?) translate to a
signal. (well, it appears the programmer has to alter the control
word to convert it to a trappable signal.) Anyway, my goal was to
allow the processor/OS to raise the exception without having to
manually test for it on every single divide operation.
The following code should enable floating point exceptions along
with a few other floating point issues (it works with vc++):
unsigned int u;
unsigned int control_word;
_controlfp_s(&control_word, 0, 0);
u = control_word & ~(_EM_INVALID | _EM_DENORMAL |
_EM_ZERODIVIDE
| _EM_OVERFLOW | _EM_UNDERFLOW /*| _EM_INEXACT*/);
_controlfp_s( &control_word, u, _MCW_EM);
Use a try catch block to capture the exception.
Perhaps an example would help me understand how to use what you
are proposing.
Regards
Damian
Thanks for your thoughts.
David