Visual C ++ / странное поведение после включения исключений с плавающей точкой (ошибка компилятора?) - PullRequest
8 голосов
/ 26 ноября 2010

Я изо всех сил пытаюсь получить надежный способ отловить исключения с плавающей запятой в 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();
}

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

Ответы [ 2 ]

10 голосов
/ 26 ноября 2010

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

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

2 голосов
/ 01 февраля 2017

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

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

...