2010-11-26 25 views
8

Visual Studio (2005 veya 2008) altındaki kayan nokta özel durumlarını yakalamanın güvenilir bir yolunu bulmaya çalışıyorum. Varsayılan olarak, görsel stüdyo altında, kayan nokta istisnaları yakalanmaz ve yakalamak oldukça zordur (temel olarak çoğu donanımsal sinyal olduğundan ve özel durumlara dönüştürülmesi gerektiğinden)Visual C++/Garip nokta özel durumları etkinleştirdikten sonra garip davranış (derleyici hata?)

İşte yaptığım şey:
-
taşıma SEH istisnalar açın (özellikler/kod üretme etkinleştirme/C++ istisnalar: SEH istisnalar ile Evet)
- yüzen aşağıdaki örnekte gösterildiği gibi şimdi (istisnaları yakalamak do

_controlfp kullanarak istisnaları işaret etkinleştirme sıfır istisna ile basit bir bölüme). Bununla birlikte, bu özel durumu yakaladığım anda, programın hataya aykırı bir şekilde bozuk olduğu görülüyor (çünkü basit float başlatma, ayrıca std :: cout çalışmayacak!).

Bu oldukça garip davranışı gösteren basit bir demo programı oluşturdum.

Not: Bu davranış, birkaç bilgisayarda çoğaltılmıştır.

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

Bu durumu düzeltmek için neler yapılabileceği konusunda bir ipucu var mı? Çok teşekkürler şimdiden!

cevap

10

Kayan nokta istisnası yakaladığınızda durum sözcüğündeki FPU özel durum işaretlerini temizlemeniz gerekir. _clearfp() öğesini çağırın.

Donanım istisnasını bir C++ istisnasına çeviren bir istisna filtresi yazmak için _set_se_translator() öğesini kullanmayı düşünün. Seçici olduğundan emin olun, sadece FPU istisnalarını çevirin.

+2

Önemli bir not, ancak: _fpreset(), kayan nokta durum sözcüğünü temizler * ve * kayan nokta matematik paketini yeniden başlatır, yani istisnalar sonradan atılmaz. Sonraki istisnaları devre dışı bırakmak için, –

+0

yerine _clearfp() kullanılabilir OQ ile aynı problemi yaşadım (bu yüzden ikiniz için büyük bir +1). –

1

Ek bilgi: 64-bit pencerelerde 32-bit kodu çalıştırıyorsanız ve/arch: SSE2 veya SSE2 komut setini ya da üst kümelerinden birini etkinleştiren diğer seçenekleri kullanıyorsanız, daha fazlasını yapmanız gerekebilir. sert sıfırlama.

Visual Studio 2015 ile (ve muhtemelen daha sonraki sürümleri), _fpreset() işlevini, _Epearfp() yerine SSE2 kayıtlarında oluşturulan kayan noktalı nokta yakalamalarından sonra çağırmanız gerekir. Bunu Visual Studio 2013 ve daha önceki bir sürümle yaparsanız, çalışma zamanı kitaplığının karışmasına neden olan çeşitli garip sorunlarla karşılaşırsınız.