Handling Floating-Point exceptions in C++

后端 未结 3 1156
独厮守ぢ
独厮守ぢ 2021-01-11 14:43

I\'m finding the floating-point model/error issues quite confusing. It\'s an area I\'m not familiar with and I\'m not a low level C/asm programmer, so I would appreciate a b

3条回答
  •  广开言路
    2021-01-11 14:44

    I can't help much with the first two questions, but I have experience and a suggestion for the question about masking FPU exceptions.

    I've found the functions

    _statusfp()  (x64 and Win32)
    _statusfp2() (Win32 only)
    _fpreset()
    _controlfp_s()
    _clearfp()
    _matherr()
    

    useful when debugging FPU exceptions and in delivering a stable and fast product.

    When debugging, I selectively unmask exceptions to help isolate the line of code where an fpu exception is generated in a calculation where I cannot avoid calling other code that unpredictably generates fpu exceptions (like the .NET JIT's divide by zeros).

    In released product I use them to deliver a stable program that can tolerate serious floating point exceptions, detect when they occur, and recover gracefully.

    I mask all FPU exceptions when I have to call code that cannot be changed,does not have reliable exception handing, and occasionally generates FPU exceptions.

    Example:

    #define BAD_FPU_EX (_EM_OVERFLOW | _EM_ZERODIVIDE | _EM_INVALID)
    #define COMMON_FPU_EX (_EM_INEXACT | _EM_UNDERFLOW | _EM_DENORMAL)
    #define ALL_FPU_EX (BAD_FPU_EX | COMMON_FPU_EX)
    

    Release code:

    _fpreset();
    Use _controlfp_s() to mask ALL_FPU_EX 
    _clearfp();
    ... calculation
    unsigned int bad_fpu_ex = (BAD_FPU_EX  & _statusfp());
    _clearfp(); // to prevent reacting to existing status flags again
    if ( 0 != bad_fpu_ex )
    {
      ... use fallback calculation
      ... discard result and return error code
      ... throw exception with useful information
    }
    

    Debug code:

    _fpreset();
    _clearfp();
    Use _controlfp_s() to mask COMMON_FPU_EX and unmask BAD_FPU_EX 
    ... calculation
      "crash" in debugger on the line of code that is generating the "bad" exception.
    

    Depending on your compiler options, release builds may be using intrinsic calls to FPU ops and debug builds may call math library functions. These two methods can have significantly different error handling behavior for invalid operations like sqrt(-1.0).

    Using executables built with VS2010 on 64-bit Windows 7, I have generated slightly different double precision arithmetic values when using identical code on Win32 and x64 platforms. Even using non-optimized debug builds with /fp::precise, the fpu precision control explicitly set to _PC_53, and the fpu rounding control explicitly set to _RC_NEAR. I had to adjust some regression tests that compare double precision values to take the platform into account. I don't know if this is still an issue with VS2012, but heads up.

提交回复
热议问题