Fragensteller
Using DBL_MAX yields to a floating-point exception when compiling with /clr

Frage
-
Hello,
I have C++ project which I want to compile with the /clr option. If I do this I get at runtime a floating-point exception. I investigate that the following code causes the exception:
double d = DBL_MAX;
The only solution I found is to turn off the Overflow exception on the FPU.
Have anyone an explanation for this effect? And is my solution correct, because I have a bad feeling to switch off the Overflow exception ;-)
Thanks,
Jens
Info:
-I work with VS2008 on a 32bit Windows Vista
-And on a non Clr project it works
Alle Antworten
-
Hallo Jochen,
vielen Dank für die Antwort, und sorry für's Englisch, habe versehentlich gedacht ich wäre auf dem englischen Forum.
Beim meinem Projekt handelt es sich um ein "altes" MFC-Project welches mit WPF-Controls aufgewertet werden soll, sprich es werden WPF-Controls geladen.
Hab mal noch folgendes ausprobiert:
Hab mir mit dem VS Wizard eine einfache MFC-Applikation erzeugt, und diese mit /clr kompiliert.
Dabei hab ich festgestellt, dass standardmäßig alle FPU Exceptions ignoriert werden, wenn ich die OVERFLOW Exception einschalte führt der Aufruf der Funktion test(), eben zu dieser OVERFLOW Exception.
Wenn ich das Ganze ohne /clr baue, funktioniert alles.
Was mir jetzt nicht ganz klar ist wieso double d = DBL_MAX; unter /clr zur OVERFLOW Exception führt?
Das betroffene MFC-Projekt ist eine DLL die von einer größeren Anwendung als eine Art Plugin verwendet wird. Diese Anwendung schalltet die Überwachung der FPU Exceptions ein. Meine Idee ist jetzt beim Aufruf der MFC-DLL die FPU-Setting auf den Standard zu setzten (alle Expections aus) und beim Verlassen der DLL wieder die original FPU Einstellungen der Anwendung herzustellen. Das scheint die einzige Lösung zu sein!?void test() { double d = DBL_MAX; } // dump function for fpu settings static CString FPUExceptionToString( const size_t arg ) { CString retVal( _T("") ); if( arg & _EM_INVALID ) retVal += _T( "invalid " ); if( arg & _EM_DENORMAL ) retVal += _T( "denormal " ); if( arg & _EM_ZERODIVIDE ) retVal += _T( "zerodivide " ); if( arg & _EM_OVERFLOW ) retVal += _T( "overflow " ); if( arg & _EM_UNDERFLOW ) retVal += _T( "underflow " ); if( arg & _EM_INEXACT ) retVal += _T( "inexact " ); return retVal; } // CTestApp construction CTestApp::CTestApp() { m_bHiColorIcons = TRUE; size_t fpuSettings = _control87( 0, 0 );// Get standard fpu settings ::AfxMessageBox( FPUExceptionToString(fpuSettings));// dump fpu settings size_t fpu_cw= _EM_INVALID | _EM_DENORMAL | _EM_ZERODIVIDE | _EM_UNDERFLOW | _EM_INEXACT; fpuSettings =_control87( fpu_cw,_MCW_EM );// ignore all exceptions exept the overflow exception ::AfxMessageBox( FPUExceptionToString(fpuSettings));// dump new fpu setting // calling this function causes the floating-point overflow exception test(); }
Danke,
Jens -
Was soll denn bei Dir "baim Aufruf" und "beim Verlassen" der DLL sein?
Das geht so nicht... wenn dann müsstest Du es bei *jedem* Codeteil von Dir siechern/ändern/wiederherstellen
Und ich kann es immer noch nicht nachvollziehen... WAS muss ich genau machen, damit dies auftritt?
Ich vermute eher, dass es an WPF liegt... den ich habe auch hier mit VB5-OCX-Controls bei floating-Point Werten Probleme... das werden wohl die gleichen sein ;(
Was ich jetzt versucht habe:
1. MFCApp per Wizard erstellen (Dialog-based)
2. Ok-Button im Designer doppel-klicken
3. Ganz oben ein
#include <float.h>
4. Der OnBnClickedOk enthält:
void CMFCAppTestCLR1Dlg::OnBnClickedOk() { double dbl = DBL_MAX; CString s; s.Format(_T("%f"), dbl); this->MessageBox(s); }
5. /clr in den Projekteinstellungen aktiviert
6. Rebuild-All
7. Ctrl-F5 (Start)
Sowohl Debug als auch Release haben das "richtige" verhalten... also keinerlei Exceptions oder sonstwas...
WAS hast Du noch gemacht?
Jochen Kalmbach (MVP VC++) -
Vielleicht hilft aber das Folgende:
http://buffered.io/2007/06/27/extended-precision-floating-point-values-in-the-clr/
Jochen Kalmbach (MVP VC++) -
Eine Exception kann ich nur mit Folgendem Code erzeugen:
Dabei wird aber die Preicison auf 32-Bits reduziert... meine andere Vermutung ist, dass dies irgendjemand macht. Beachte: Wenn dies in *irgendeiner* DLL gemacht wird, so wirkt sich dies auf *alle* DLLs des Prozesses aus!
Du kannst Dir mit dem Folgenden Code auch anzeigen lassen, wie die Floating-Point Register eingestellt sind. Normal ist 0x9001f.
unsigned int prevState; // set the FPU state to 64-bit _controlfp_s( &prevState, 0, 0); // 0x9001F (=> Default: _PC_53) _controlfp_s( &prevState, _PC_24, MCW_PC); // => Nur hier bekomme ich eine Exception... //_controlfp_s( &prevState, _PC_53, MCW_PC); //_controlfp_s( &prevState, _PC_64, MCW_PC); double dbl = 0; dbl += DBL_MAX; CString sz; sz.Format(_T("%f: 0x%x"), dbl, prevState); // Restore floating-point state _controlfp_s( NULL, prevState, MCW_PC); this->MessageBox(sz);
Jochen Kalmbach (MVP VC++) -
Also ich hab jetzt mal Dein Beispiel nachgebaut und wie folgt erweiter:
static void test() { double dbl = 0;
1. Wenn ich nur Percision runtersetzte, so wie bei Deinem original Beispiel (und die Zeile _controlfp_s( &prevState, fpu_cw, _MCW_EM); auskommentiert ist) bekomme ich keine Exception.
dbl += DBL_MAX; CString s; s.Format(_T("%f"), dbl); ::AfxMessageBox(s); } void CFPU_TESTDlg::OnBnClickedOk() { CString sz; unsigned int prevState; // set the FPU state to 64-bit _controlfp_s( &prevState, 0, 0); // 0x9001F (=> Default: _PC_53) sz.Format(_T("Default FPU values = 0x%x"),prevState); // 0x9001f ::AfxMessageBox(sz); //_controlfp_s( &prevState, _PC_24, MCW_PC); // => Nur hier bekomme ich eine Exception... //_controlfp_s( &prevState, _PC_53, MCW_PC); //_controlfp_s( &prevState, _PC_64, MCW_PC); size_t fpu_cw= _EM_INVALID | _EM_DENORMAL | _EM_ZERODIVIDE | _EM_UNDERFLOW | _EM_INEXACT; _controlfp_s( &prevState, fpu_cw, _MCW_EM); // => Nur wenn ich die FPU so setzte kommt es zur Exception sz.Format(_T("Changed FPU values = 0x%x"),prevState); // 0x9001b ::AfxMessageBox(sz); // call the problematic test function test(); // Restore floating-point state _controlfp_s( NULL, prevState, MCW_PC); }
Aber was mir aufgefallen ist, ist dass wenn ich:
double dbl = DBL_MAX; schreibe der Wert korrekt ausgegeben wird.
Wenn ich dagegen
double dbl = 0;
dbl += DBL_MAX; schreibe wird 1.#INF00 ausgegeben.
2. Wen ich die Percision auf dem Default _PC_53 belasse und die _EM_OVERFLOW Exception einschalte also quasi
_controlfp_s( &prevState, _PC_24, MCW_PC); auskommentieren
und
_controlfp_s( &prevState, fpu_cw, _MCW_EM); einkommentieren
dann tritt eben diese Exception auf:
First-chance exception at 0x5cfaa0f0 in FPU_TEST.exe: 0xC0000091: Floating-point overflow.
Ach, und das alles passiert nur unter Debug.
Könnte das evtl. irgedwie damit zusammen hängen?
http://support.microsoft.com/kb/167995/en-us- Bearbeitet Robert BreitenhoferModerator Sonntag, 17. Januar 2010 18:33 Formatierung
-
Hallo,
ich muss gestehen: ich habe gerade zu wenig Zeit um mir das genauer anzuschauen... hast Du eine MSDN-Subscription? Wenn ja, dann melde Dich doch mal beim MS-Produkt-Support. In der Subscriptions hast Du 2-4 Anfragen im Jahr frei.
Die sollte man auch aussnützen ;)
Ansonsten meldest Dich hier einfach nochmals, vielleicht kann ich mir es ja doch mal anschauen...
Jochen Kalmbach (MVP VC++)