Archives
D Programming
DD.gnu digitalmars.D digitalmars.D.bugs digitalmars.D.dtl digitalmars.D.ide digitalmars.D.dwt digitalmars.D.announce digitalmars.D.learn digitalmars.D.debugger C/C++ Programming
c++c++.announce c++.atl c++.beta c++.chat c++.command-line c++.dos c++.dos.16-bits c++.dos.32-bits c++.idde c++.mfc c++.rtl c++.stl c++.stl.hp c++.stl.port c++.stl.sgi c++.stlsoft c++.windows c++.windows.16-bits c++.windows.32-bits c++.wxwindows digitalmars.empire digitalmars.DMDScript electronics |
c++ - Traping divide by zero
== 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 Jan 16 2008
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; } //----------------------------------------------------------------- Jan 18 2008
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. Jan 22 2008
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 Jan 23 2008
== Quote from Sunny Pal Singh (arorasp2000 hotmail.com)'s articleC++ 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 Jan 24 2008
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. Feb 08 2008
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 Feb 13 2008
== Quote from Damian (damian.dixon gmail.com)'s articleHi, Good practice is to check for division by zero before it occurs. Mar 17 2008
|