Visual C++ / 启用浮点异常后出现奇怪的行为(编译器错误?)

发布于 2024-10-04 03:00:00 字数 2499 浏览 2 评论 0原文

我正在努力寻找一种可靠的方法来捕获 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();
}

有谁知道可以采取什么措施来纠正这种情况? 非常感谢!

I am struggling to get a reliable way to catch floating points exceptions under Visual Studio (2005 or 2008). By default, under visual studio, floating point exceptions are not caught, and they are quite hard to catch (mainly because most of them are hardware signal, and need to be translated into exceptions)

Here is what I did :
- Turn on SEH exceptions handling
(properties / code generation / Enable C++ Exceptions : Yes with SEH Exceptions)
- Activate floating points exceptions using _controlfp

I do now catch the exceptions (as shown in the example below which a simple divide by zero exception).
However, as soon as I catch this exception, it seems that the program is irremediably corrupted (since simple float initialization, as well as std::cout won't work!).

I have built a simple demo program that shows this rather weird behavior.

Note : this behavior was reproduced on several computers.

#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();
}

Does anyone have a clue on what could be done to correct this situation?
Many thanks in advance!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

没有心的人 2024-10-11 03:00:00

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

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

You have to clear the FPU exception flags in the status word when you catch a floating point exception. Call _clearfp().

Consider using _set_se_translator() to write an exception filter that translate the hardware exception to a C++ exception. Be sure to be selective, only translate the FPU exceptions.

紫南 2024-10-11 03:00:00

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

使用 Visual Studio 2015(以及 VS.2022 的更高版本),您需要在 SSE2 寄存器中生成浮点陷阱后调用 _fpreset(),而不仅仅是 _clearfp()。如果您使用 Visual Studio 2013 及更早版本执行此操作,您会遇到各种奇怪的问题,这是由于运行时库变得混乱而导致的。

Additional information: If you are running 32-bit code on 64-bit windows, and use /arch:SSE2 or other options that enable the SSE2 instruction set, or one of its supersets, you may need to do a more drastic reset.

With Visual Studio 2015 (and later versions as far as VS.2022), you need to call _fpreset() after floating-point traps generated in the SSE2 registers, rather than just _clearfp(). If you do this with Visual Studio 2013 and earlier, you get a variety of weird problems, caused by the run-time library getting confused.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文