Floating Point Exceptions



  • Ich versuche vezweifelt Floating Point Exceptions in C++ Exceptions umzuwandeln. Allerdings fängt mein Testcode keine einzige Exception, wobei es so aussieht, als würde gar keine erzeugt (die Routine zur Übersetzung der Floating Point Exception in eine C++ Exception wird nie aufgerufen). Was mache ich falsch?

    Plattform: Visua Studio 2010, WindowsXP, Core i5 CPU. Befehlszeile:
    /ZI /nologo /W3 /WX- /Od /Oy- /D "_MBCS" /Gm /EHa /RTC1 /GS /fp:precise /fp:except /Zc:wchar_t /Zc:forScope /Fp"Debug\AccessAndValidationTest.pch" /Fa"Debug\" /Fo"Debug\" /Fd"Debug\vc100.pdb" /Gd /analyze- /errorReport:queue

    Hier mein Code:

    test.cpp:

    #include <vector>
    #include <exception>
    #include <iostream>
    #include <iomanip>
    
    #include "floatingPointExceptions.h"
    
    using namespace fpetestenv;
    
    using std::vector;
    using std::exception;
    using std::out_of_range;
    using std::cout;
    using std::endl;
    using std::setbase;
    
    #pragma float_control( except, on )
    
    int main(void)
    {
    	initializeFloatingPointExceptionHandling();
    
    	unsigned int fpControlWord = 0;
    	_controlfp_s( &fpControlWord, 0, 0);
    	cout << "fpcw = 0x" << setbase(16) << fpControlWord << endl;
    	try
    	{
    		double inexact = 1.0/3.0;
    		cout << "FPU Status is: 0x" << setbase(16) << _statusfp() << endl;
    		_clearfp();
    		double invalid = 0.0/sin(0.0);
    		cout << "FPU Status is: 0x" << setbase(16) << _statusfp() << endl;
    		_clearfp();
    		double ovflow = DBL_MAX/DBL_MIN;
    		cout << "FPU Status is: 0x" << setbase(16) << _statusfp() << endl;
    		_clearfp();
    		double underflow = DBL_MIN/DBL_MAX;
    		cout << "FPU Status is: 0x" << setbase(16) << _statusfp() << endl;
    		_clearfp();
    		double divByZero = 1/sin(0.0);
    		cout << "FPU Status is: 0x" << setbase(16) << _statusfp() << endl;
    		_clearfp();
    	}
    	catch(fe_divide_by_zero)
    	{
    		cout << "fe_divide_by_zero exception detected" << endl;
    	}
    	catch(float_exception)
    	{
    		cout << "float_exception exception detected" << endl;
    	}
    
    	return 0;
    }
    

    Die Übersetzungsroutine:

    #include "floatingPointExceptions.h"
    #include <Windows.h>
    #include <float.h>
    #include <fpieee.h>
    
    namespace fpetestenv
    {
    	#pragma float_control( except, on )
    	// nameing according to Microsoft Example
    	void se_fe_trans_func( unsigned int u, EXCEPTION_POINTERS* pExp )
    	{
    		switch (u)
    		{
    		case STATUS_FLOAT_DENORMAL_OPERAND: 
    			throw fe_denormal_operand();
    		case STATUS_FLOAT_DIVIDE_BY_ZERO: 
    			throw fe_divide_by_zero();
    		case STATUS_FLOAT_INEXACT_RESULT: 
    			throw fe_inexact_result();	
    		case STATUS_FLOAT_INVALID_OPERATION: 
    			throw fe_invalid_operation();	
    		case STATUS_FLOAT_OVERFLOW: 
    			throw fe_overflow();	
    		case STATUS_FLOAT_UNDERFLOW: 
    			throw fe_underflow();	
    		case STATUS_FLOAT_STACK_CHECK: 
    			throw fe_stack_check();	
    		default:
    			throw float_exception();
    		};
    	}
    	#pragma float_control( except, on )
    	void initializeFloatingPointExceptionHandling()
    	{
    		unsigned int fpControlWord = 0x00;
    
    		_clearfp(); // always call _clearfp before enabling/unmasking a FPU exception
    		errno_t success = _controlfp_s( &fpControlWord, _EM_INVALID 
    			                       | _EM_ZERODIVIDE 
    								   | _EM_DENORMAL
    								   | _EM_OVERFLOW
    								   | _EM_UNDERFLOW
    								   | _EM_INEXACT, _MCW_EM );
    
    		_se_translator_function old =_set_se_translator(se_fe_trans_func);
    	}
    }
    

    Und der Header dazu:

    #ifndef FLOATING_POINT_EXCEPTIONS_H
    #define FLOATING_POINT_EXCEPTIONS_H
    
    #include <exception>
    
    using std::exception;
    namespace fpetestenv
    {
    	void initializeFloatingPointExceptionHandling();
    
    	class float_exception : public exception
    	{
    	};
    
    	class fe_denormal_operand : public float_exception
    	{
    	};
    
    	class fe_divide_by_zero : public float_exception 
    	{
    	};
    
    	class fe_inexact_result : public float_exception 
    	{
    	};
    
    	class fe_invalid_operation : public float_exception 
    	{
    	};
    
    	class fe_overflow : public float_exception 
    	{
    	};
    
    	class fe_stack_check : public float_exception 
    	{
    	};
    
    	class fe_underflow : public float_exception 
    	{
    	};
    }
    #endif
    


  • Wofür brauchst du das überhaupt? Abgefangen wird aber keine, weil nie eine geworfen wird. Du kannst immer nur etwas allgemeineres abfangen, nichts spezielleres.



  • Ich brauche das um DivisionByZero, Overflow, Underflow etc. bei Berechnungen mit Float und Double erkennen zu können.

    Den letzten Satz Deiner Antwort habe ich leider nicht verstanden.

    Lt Debugger liegt das Problem schon früher. Z.B. müsste bei double invalid = 0.0/sin(0.0); eine fe_divide_by_zero Exception geworfen werden. Die würde über se_fe_trans_func() erzeugt. Aber die Funktion wird nie aufgerufen, obwohl sie mit initializeFloatingPointExceptionHandling() als Floating Point Exception Handling Routine gesetzt wird. (Anmerkung: Floating Point Exceptions haben erst mal gar nichts mit C++ zu tun, sondern sind Interrupts, die die FPU bei bestimmten Situationen, z.B. Division durch Null, auslöst).



  • gastgast schrieb:

    (Anmerkung: Floating Point Exceptions haben erst mal gar nichts mit C++ zu tun, sondern sind Interrupts, die die FPU bei bestimmten Situationen, z.B. Division durch Null, auslöst).

    Nun, darüber bin ich mir durchaus im klaren. Du hast aber doch eigene exception Klassen für diese Fehler erstellt. Anhand des von dir geposteten Codes wäre mir auch schleierhaft von wem, wann und wo deine se_fe_trans_func Funktion aufgerufen werden sollte. Erläuter das mal.

    Das Einzige was ich hier sehe ist, dass du das x87 u. ggf. SSE2 Status Word neu setzt.

    MSDN schrieb:

    For the _MCW_EM mask, clearing the mask sets the exception, which allows the hardware exception; setting the mask hides the exception. Note that if a _EM_UNDERFLOW or _EM_OVERFLOW occurs, no hardware exception is thrown until the next floating-point instruction is executed. To generate a hardware exception immediately after _EM_UNDERFLOW or _EM_OVERFLOW, call the FWAIT MASM instruction.



  • *wühlt sich durch die MSDN*
    Wenn ich das richtig verstanden habe, will er mit _controlfp_s() die FP-Exceptions einschalten und mit _set_se_translator() seine Funktion als Behandlungsroutine für diese Exceptions festlegen - die dann bei einem entsprechenden Interrupt eine C++ Exception werfen soll.

    @gast:

    MSDN schrieb:

    For the _MCW_EM mask, clearing the mask sets the exception, which allows the hardware exception; setting the mask hides the exception.

    -> wenn ich das richtig verstanden habe, hast du mit deiner Aktion die FP-Exceptions abgeschaltet.



  • Nun, dass kommt noch hinzu. Wenn, dann müsstes er 0 übergeben, insofern ich seine Intention richtig verstanden habe. Anhand des Codes ist damit aber immer noch nicht die Verbindung zu seinen Behandlungsroutinen hergestellt. Die Implementation von _set_se_translator() sehe ich nicht.

    Mir wird ganz anders:

    extern "C" int my_handler(_FPIEEE_RECORD*)
    {
    }
    
    _controlfp(~_EM_ZERODIVIDE, _MCW_EM);
    __try
    {
    }
    __except(_fpieee_flt(GetExceptionCode(), GetExceptionInformation(), my_handler))
    {
    }
    


  • Alternativ gibt es auch noch die Funktion _fpieee_flt zum Fangen der FP-Exceptions...

    Edit: @FrEEzE2046, das war jetzt unfair, da du deinen Beitrag nachträglich editiert hast 😉

    P.S. _set_se_translator ist eine Standard-Funktion



  • *vordieStirnSchlag* CStoll, das war's, hatte ich in der Doku überlesen. Jetzt klappt's mit

    errno_t success = _controlfp_s( &fpControlWord, 0xffffffff^(_EM_INVALID 
    			                       | _EM_ZERODIVIDE 
    								   | _EM_DENORMAL
    								   | _EM_OVERFLOW
    								   | _EM_UNDERFLOW
    								   | _EM_INEXACT), _MCW_EM );
    

    wie gewünscht. Super, vielen Dank Euch beiden, für Eure Unterstützung!

    (Denormal wird mit _controlfp_s zwar nicht unterstützt, aber das ist mir ATM auch nicht wichtig.)



  • Th69 schrieb:

    Edit: @FrEEzE2046, das war jetzt unfair, da du deinen Beitrag nachträglich editiert hast 😉

    Hey, ohne Witz, ich hab einfach länger für das Schreiben des Beispiels benötigt. Als ich editiert hab, war unter mir noch freie Luft 😉

    Bzgl. _set_se_translator. Ja, du hast recht, es ist tatsächlich eine Standard Funktion.


Anmelden zum Antworten