Visual C++ / странное поведение после включения исключений с плавающей запятой (ошибка компилятора)
Я изо всех сил пытаюсь получить надежный способ поймать исключения с плавающими точками в Visual Studio (2005 или 2008). По умолчанию в visual studio исключения с плавающей запятой не перехватываются, и их довольно сложно поймать (в основном потому, что большинство из них являются аппаратным сигналом и должны быть переведены в исключения)
вот что я сделал :
- Включить обработку исключений SEH
(свойства / генерация кода / включение исключений C++: да с SEH Исключения)
- Активировать исключения с плавающими точками с помощью _controlfp
теперь я ловлю исключения (как показано в примере, ниже которого простое деление на нулевое исключение). Однако, как только я поймаю это исключение, кажется, что программа непоправимо повреждена (так как простая инициализация float, а также 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();
}
кто-нибудь знает, что можно сделать, чтобы исправить эту ситуацию? Большое спасибо заранее!
2 ответов
вы должны очистить флаги исключений FPU в слове состояния, когда вы ловите исключение с плавающей запятой. Вызов _clearfp().
рассмотрите возможность использования _set_se_translator() для записи фильтра исключений, который преобразует аппаратное исключение в исключение C++. Обязательно будьте избирательны, переводите только исключения FPU.
дополнительная информация: Если вы используете 32-разрядный код в 64-разрядной windows и используете /arch:SSE2 или другие параметры, которые включают набор инструкций SSE2 или один из его суперсетов, вам может потребоваться выполнить более резкий сброс.
в Visual Studio 2015 (и, предположительно, более поздних версиях) вам нужно вызвать _fpreset () после ловушек с плавающей запятой, созданных в регистрах SSE2, а не только _clearfp (). Если вы сделаете это с Visual Studio 2013 и ранее, вы получите множество странных проблемы, вызванные тем, что библиотека времени выполнения путается.