8

我正在努力寻找一种可靠的方法来在 Visual Studio(2005 或 2008)下捕获浮点异常。默认情况下,在visual studio下,浮点异常是不被捕获的,而且很难捕获(主要是因为大部分是硬件信号,需要翻译成异常)

这是我所做的:
- 打开 SEH 异常处理
(属性/代码生成/启用 C++ 异常:是的,带有 SEH 异常)
- 使用 _controlfp 激活浮点异常

我现在确实捕获了异常(如下例所示,这是一个简单的除以零异常)。然而,一旦我发现这个异常,似乎程序已经不可挽回地损坏了(因为简单的浮点初始化以及 std::cout 都不起作用!)。

我已经构建了一个简单的演示程序来展示这种相当奇怪的行为。

注意:此行为在多台计算机上重现。

#include "stdafx.h"
#include <math.h>

#include <float.h>
#include <iostream>


using namespace std;


//cf http://www.fortran-2000.com/ArnaudRecipes/CompilerTricks.html#x86_FP
//cf also the "Numerical Recipes" book, which gives the same advice 
    //on how to activate fp exceptions
void TurnOnFloatingExceptions()
{
  unsigned int cw;
  // Note : same result with controlfp
  cw = _control87(0,0) & MCW_EM;
  cw &= ~(_EM_INVALID|_EM_ZERODIVIDE|_EM_OVERFLOW);
  _control87(cw,MCW_EM);

}

//Simple check to ensure that floating points math are still working
void CheckFloats()
{
  try
  {
         // this simple initialization might break 
         //after a float exception!
    double k = 3.; 
    std::cout << "CheckFloatingPointStatus ok : k=" << k << std::endl;
  }  
  catch (...)
  {
    std::cout << " CheckFloatingPointStatus ==> not OK !" << std::endl;
  }
}


void TestFloatDivideByZero()
{
  CheckFloats();
  try
  {
    double a = 5.;
    double b = 0.;
    double c = a / b; //float divide by zero
    std::cout << "c=" << c << std::endl; 
  }
  // this catch will only by active:
  // - if TurnOnFloatingExceptions() is activated 
  // and 
  // - if /EHa options is activated
  // (<=> properties / code generation / Enable C++ Exceptions : Yes with SEH Exceptions)
  catch(...)
  {         
    // Case 1 : if you enable floating points exceptions ((/fp:except)
    // (properties / code generation / Enable floting point exceptions)
    // the following line will not be displayed to the console!
    std::cout <<"Caught unqualified division by zero" << std::endl;
  }
  //Case 2 : if you do not enable floating points exceptions! 
  //the following test will fail! 
  CheckFloats(); 
}


int _tmain(int argc, _TCHAR* argv[])
{
  TurnOnFloatingExceptions();
  TestFloatDivideByZero();
  std::cout << "Press enter to continue";//Beware, this line will not show to the console if you enable floating points exceptions!
  getchar();
}

有没有人知道可以做些什么来纠正这种情况?提前谢谢了!

4

2 回答 2

11

捕获浮点异常时,必须清除状态字中的 FPU 异常标志。调用 _clearfp()。

考虑使用 _set_se_translator() 编写一个异常过滤器,将硬件异常转换为 C++ 异常。一定要有选择性,只翻译 FPU 异常。

于 2010-11-26T18:12:01.340 回答
2

附加信息:如果您在 64 位窗口上运行 32 位代码,并使用 /arch:SSE2 或其他启用 SSE2 指令集或其超集之一的选项,您可能需要进行更彻底的重置。

使用 Visual Studio 2015(可能还有更高版本),您需要在 SSE2 寄存器中生成浮点陷阱后调用 _fpreset(),而不仅仅是 _clearfp()。如果您使用 Visual Studio 2013 及更早版本执行此操作,则会遇到各种奇怪的问题,这些问题是由运行时库混淆引起的。

于 2017-02-01T17:28:48.313 回答