2010-11-26 4 views
8

Я изо всех сил пытаюсь получить надежный способ получить исключения с плавающей запятой в Visual Studio (2005 или 2008). По умолчанию в визуальной студии исключения с плавающей запятой не пойманы, и их довольно сложно поймать (главным образом потому, что большинство из них являются аппаратным сигналом и должны быть переведены на исключения)Visual C++/Weird поведение после включения исключений с плавающей запятой (ошибка компилятора?)

Вот что я сделал:
- Включите SEH исключения обработки
(генерирующие свойства/код/​​Enable 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(); 
} 

Кто-нибудь знает, что можно сделать, чтобы исправить эту ситуацию? Большое спасибо заранее!

ответ

10

Вы должны очистить флаги исключения FPU в слове состояния, когда вы поймаете исключение с плавающей запятой. Вызовите _clearfp().

Рассмотрите возможность использования _set_se_translator() для создания фильтра исключений, который переводит аппаратное исключение в исключение C++. Убедитесь, что вы выборочны, только переводите исключения FPU.

+2

Одно важное замечание: _fpreset() очистит слово состояния с плавающей запятой * и * повторно инициализирует математический пакет с плавающей запятой, то есть исключения не будут выбраны впоследствии. Чтобы не отключать последующие исключения, вместо этого можно использовать _clearfp(). – 2011-10-31 23:44:44

1

Дополнительная информация: Если вы используете 32-разрядный код в 64-битных окнах и используете/arch: SSE2 или другие параметры, которые позволяют установить набор инструкций SSE2 или один из его надмножеств, вам может потребоваться сделать больше резкий сброс.

С Visual Studio 2015 (и, предположительно, более поздними версиями) вам необходимо вызвать _fpreset() после ловушек с плавающей запятой, сгенерированных в регистрах SSE2, а не просто _clearfp(). Если вы сделаете это с помощью Visual Studio 2013 и более ранних версий, вы получите множество странных проблем, вызванных запутанием библиотеки времени выполнения.