Visual C++ / 启用浮点异常后出现奇怪的行为(编译器错误?)
我正在努力寻找一种可靠的方法来捕获 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
当捕获浮点异常时,必须清除状态字中的 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.附加信息:如果您在 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.