2010-11-26 5 views
8

Ich habe Schwierigkeiten, eine zuverlässige Möglichkeit zu erhalten, Gleitkommaausnahmen unter Visual Studio (2005 oder 2008) zu fangen. In Visual Studio werden Gleitkommaausnahmen standardmäßig nicht abgefangen, und sie sind ziemlich schwer zu fangen (hauptsächlich, weil die meisten Hardwaresignale sind und in Ausnahmen übersetzt werden müssen).Visual C++/Seltsames Verhalten nach dem Aktivieren von Gleitkommaausnahmen (Compilerfehler?)

Hier ist, was ich getan habe:
- Schalen Sie SEH Behandlung von Ausnahmen
(Eigenschaften/Code-Generierung aktivieren/C++ Ausnahmen: Ja mit SEH Ausnahmen)
- Aktivieren Gleitpunkte Ausnahmen mit _controlfp

ich fange nun die Ausnahmen (wie im Beispiel unten gezeigt was eine einfache Division durch Null Ausnahme). Sobald ich jedoch diese Ausnahme abfangen, scheint es, dass das Programm irreversibel beschädigt ist (da einfache Float-Initialisierung sowie std :: cout nicht funktionieren!).

Ich habe ein einfaches Demo-Programm gebaut, das dieses seltsame Verhalten zeigt.

Hinweis: Dieses Verhalten wurde auf mehreren Computern reproduziert.

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

Hat jemand eine Ahnung, was getan werden könnte, um diese Situation zu korrigieren? Vielen Dank im Voraus!

Antwort

10

Sie müssen die FPU-Ausnahmeflags im Statuswort löschen, wenn Sie eine Gleitkommaausnahme abfangen. Rufen Sie _clearfp() auf.

Verwenden Sie _set_se_translator(), um einen Ausnahmefilter zu schreiben, der die Hardwareausnahme in eine C++ - Ausnahme übersetzt. Stellen Sie sicher, dass Sie selektiv sind und übersetzen Sie nur die FPU-Ausnahmen.

+2

Eine wichtige Anmerkung jedoch: _fpreset() löscht das Fließkomma-Statuswort * und * reinitialisiert das Fließkomma-Mathe-Paket, d. H. Es werden keine Ausnahmen nachträglich ausgelöst. Um nachfolgende Ausnahmen nicht zu deaktivieren, kann stattdessen _clearfp() verwendet werden –

+0

Ich hatte das gleiche Problem wie der OQ (also ein großer +1 für euch beide). –

1

Zusätzliche Informationen: Wenn Sie 32-Bit-Code in 64-Bit-Fenstern ausführen und/arch: SSE2 oder andere Optionen verwenden, die den SSE2-Befehlssatz oder einen seiner Obermengen aktivieren, müssen Sie möglicherweise einen weiteren Befehl ausführen drastischer Reset.

Mit Visual Studio 2015 (und vermutlich späteren Versionen) müssen Sie _fpreset() nach in den SSE2-Registern generierten Gleitkommafallen aufrufen, anstatt nur _clearfp(). Wenn Sie dies mit Visual Studio 2013 und früher tun, erhalten Sie eine Vielzahl von seltsamen Problemen, die durch die Run-Time-Library verursacht werden, die verwirrt wird.