%% options copyright owner = Dirk Krause copyright year = 2014 license = bsd %% header /** @file dk3mafpe.h Floating point exception handling. Before doing any floating point calculation, use dk3mafpe_get_handling_type() to check whether an exception handling mechanism is available. The function returns DK3_MA_FPE_HANDLING_CLEARFP on Windows systems if _clearfp() and _statusfp() are available. It returns DK3_MA_FPE_HANDLING_FETESTEXCEPT if the POSIX floating point exception functions fetestexcept() and feclearexcept() are available. If none of these mechanisms is available, DK3_MA_FPE_HANDLING_NONE is returned. Your program should issue a warning. Before doing a floating point calculation use dk3mafpe_clear() to clear the FPU status word. After doing a floating point calculation use dk3mafpe_get() or dk3mafpe_get_and_clear() to retrieve the status word. Check the status word against DK3MAFPE_ALL (and-combine them) to see if any exception occured. If your calculation results in just one double you can use dk3mafpe_isfinite() and/or dk3mafpe_isnormal() to check the value. If the dk3mafpe_isfinite() test fails, the value is of no use. If the dk3mafpe_isnormal() test fails, the result is very small, it uses denormalized representation and has probably and likely lost much of it's precision. */ #include #if DK3_HAVE_FLOAT_H #include #endif #if DK3_HAVE_FENV_H #include #endif #if DK3_ON_WINDOWS /** Floating point exceptions set. */ typedef unsigned int dk3_ma_fpe_t; #if DK3_HAVE__CLEARFP /** Division by 0. */ #define DK3MAFPE_DIVBYZERO (_SW_ZERODIVIDE) /** Inexact result (either overflow or underflow). */ #define DK3MAFPE_INEXACT (_SW_INEXACT) /** Not a number (0/0 or infty - infty). */ #define DK3MAFPE_INVALID (_SW_INVALID) /** Result too large for representation. */ #define DK3MAFPE_OVERFLOW (_SW_OVERFLOW) /** Result too small for normalized representation. */ #define DK3MAFPE_UNDERFLOW (_SW_UNDERFLOW) /** Denormalized number. */ #define DK3MAFPE_DENORMAL (_SW_DENORMAL) /** Or-combination of all supported exceptions. */ #define DK3MAFPE_ALL \ ((_SW_ZERODIVIDE) \ | (_SW_INEXACT) \ | (_SW_INVALID) \ | (_SW_OVERFLOW) \ | (_SW_UNDERFLOW) \ | (_SW_DENORMAL)) #else /** Division by 0. */ #define DK3MAFPE_DIVBYZERO 0 /** Inexact result (either overflow or underflow). */ #define DK3MAFPE_INEXACT 0 /** Not a number (0/0 or infty - infty). */ #define DK3MAFPE_INVALID 0 /** Result too large for representation. */ #define DK3MAFPE_OVERFLOW 0 /** Result too small for normalized representation. */ #define DK3MAFPE_UNDERFLOW 0 /** Denormalized number. */ #define DK3MAFPE_DENORMAL 0 /** Or-combination of all supported exceptions. */ #define DK3MAFPE_ALL 0 #endif #else /** Floating point exceptions set. */ typedef int dk3_ma_fpe_t; #if DK3_HAVE_FETESTEXCEPT /** Division by 0. */ #define DK3MAFPE_DIVBYZERO (FE_DIVBYZERO) /** Inexact result (either overflow or underflow). */ #define DK3MAFPE_INEXACT (FE_INEXACT) /** Not a number (0/0 or infty - infty). */ #define DK3MAFPE_INVALID (FE_INVALID) /** Result too large for representation. */ #define DK3MAFPE_OVERFLOW (FE_OVERFLOW) /** Result too small for normalized representation. */ #define DK3MAFPE_UNDERFLOW (FE_UNDERFLOW) /** Denormalized number. */ #define DK3MAFPE_DENORMAL 0 /** Or-combination of all supported exceptions. */ #define DK3MAFPE_ALL (FE_ALL_EXCEPT) #else /** Division by 0. */ #define DK3MAFPE_DIVBYZERO 0 /** Inexact result (either overflow or underflow). */ #define DK3MAFPE_INEXACT 0 /** Not a number (0/0 or infty - infty). */ #define DK3MAFPE_INVALID 0 /** Result too large for representation. */ #define DK3MAFPE_OVERFLOW 0 /** Result too small for normalized representation. */ #define DK3MAFPE_UNDERFLOW 0 /** Denormalized number. */ #define DK3MAFPE_DENORMAL 0 /** Or-combination of all supported exceptions. */ #define DK3MAFPE_ALL 0 #endif #endif /** Different handling types for floating point exceptions. */ enum { /** No fpe exception handling available. */ DK3_MA_FPE_HANDLING_NONE = 0, /** POSIX exception handling using fetestexcept() and friends. */ DK3_MA_FPE_HANDLING_FETESTEXCEPT, /** Windows exception handling using _clearfp() and friends. */ DK3_MA_FPE_HANDLING_CLEARFP }; #ifdef __cplusplus extern "C" { #endif /** Retrieve exception set. @return Exceptions found. */ dk3_ma_fpe_t dk3mafpe_get(void); /** Clear exception set. */ void dk3mafpe_clear(void); /** Retrieve exception set and clear. @return Exceptions found. */ dk3_ma_fpe_t dk3mafpe_get_and_clear(void); /** Get exception handling type. @return One from DK3_MA_FPE_HANDLING_NONE, DK3_MA_FPE_HANDLING_FETESTEXCEPT, DK3_MA_FPE_HANDLING_CLEARFP. */ int dk3mafpe_get_handling_type(void); /** Check whether a number is normalized. @param x Number to check. @return Non-null result on success, 0 on error. */ int dk3mafpe_isnormal(double x); /** Check whether a number is finite (not NAN, not INF). @param x Number to check. @return Non-null result on success, 0 on error. */ int dk3mafpe_isfinite(double x); #ifdef __cplusplus } #endif %% module #include "dk3mafpe.h" $!trace-include #if TRACE_DEBUG static void dk3mafpe_debug(dk3_ma_fpe_t fpes) { $? "+ dk3mafpe_debug" if (NULL != dktrace_file()) { if (0 != (fpes & DK3MAFPE_DIVBYZERO)) { fputs("DIVBYZERO\n", dktrace_file()); } if (0 != (fpes & DK3MAFPE_INEXACT)) { fputs("INEXACT\n", dktrace_file()); } if (0 != (fpes & DK3MAFPE_OVERFLOW)) { fputs("OVERFLOW\n", dktrace_file()); } if (0 != (fpes & DK3MAFPE_UNDERFLOW)) { fputs("UNDERFLOW\n", dktrace_file()); } if (0 != (fpes & DK3MAFPE_INVALID)) { fputs("INVALID\n", dktrace_file()); } } $? "- dk3mafpe_debug" } #endif dk3_ma_fpe_t dk3mafpe_get(void) { #if DK3_ON_WINDOWS #if DK3_HAVE__CLEARFP #if TRACE_DEBUG dk3_ma_fpe_t back; back = _statusfp(); dk3mafpe_debug(back); return back; #else return (_statusfp()); #endif #else return 0; #endif #else #if DK3_HAVE_FETESTEXCEPT #if TRACE_DEBUG dk3_ma_fpe_t back; back = fetestexcept(FE_ALL_EXCEPT); dk3mafpe_debug(back); return back; #else return (fetestexcept(FE_ALL_EXCEPT)); #endif #else return 0; #endif #endif } void dk3mafpe_clear(void) { #if DK3_ON_WINDOWS #if DK3_HAVE__CLEARFP (void)_clearfp(); #else #endif #else #if DK3_HAVE_FETESTEXCEPT feclearexcept(FE_ALL_EXCEPT); #else #endif #endif } dk3_ma_fpe_t dk3mafpe_get_and_clear(void) { #if DK3_ON_WINDOWS #if DK3_HAVE__CLEARFP #if TRACE_DEBUG dk3_ma_fpe_t back; back = _clearfp(); dk3mafpe_debug(back); return back; #else return (_clearfp()); #endif #else return 0; #endif #else #if DK3_HAVE_FETESTEXCEPT dk3_ma_fpe_t back; back = fetestexcept(FE_ALL_EXCEPT); feclearexcept(FE_ALL_EXCEPT); #if TRACE_DEBUG dk3mafpe_debug(back); #endif return back; #else return 0; #endif #endif } int dk3mafpe_get_handling_type(void) { #if DK3_ON_WINDOWS #if DK3_HAVE__CLEARFP return DK3_MA_FPE_HANDLING_CLEARFP; #else return DK3_MA_FPE_HANDLING_NONE; #endif #else #if DK3_HAVE_FETESTEXCEPT return DK3_MA_FPE_HANDLING_FETESTEXCEPT; #else return DK3_MA_FPE_HANDLING_NONE; #endif #endif } int dk3mafpe_isfinite(double x) { #if DK3_ON_WINDOWS int back = 0; if (_finite(x)) { back = 1; } return back; #else #if DK3_HAVE_ISFINITE int back = 0; if (isfinite(x)) { back = 1; } return back; #else #error "No function or macro isfinite(x) available!" return 1; #endif #endif } int dk3mafpe_isnormal(double x) { #if DK3_ON_WINDOWS int back = 0; if (_finite(x)) { back = 1; switch (_fpclass(x)) { case _FPCLASS_ND: case _FPCLASS_PD: { back = 0; } break; } } return back; #else #if DK3_HAVE_ISNORMAL int back = 0; if (isnormal(x)) { back = 1; } return back; #else #error "No function or macro isnormal(x) available!" return 1; #endif #endif }