/** * @brief Additional ensures for scientific/engineering applications. * @author Joerg * @date 07/04/2008 */ #ifndef TUT_Float_H_GUARD #define TUT_Float_H_GUARD #include #include namespace tut { namespace detail { template struct If { typedef Else type; }; template struct If { typedef Then type; }; template struct fpt_traits { struct StdNumericLimitsNotAvailable {}; static const StdNumericLimitsNotAvailable static_check[ std::numeric_limits::is_specialized ]; static const T zero; typedef typename If::is_integer, double, T>::type Result; static T abs(const T &arg) { if(arg < zero) return zero - arg; else return arg; } static T sig(const T &arg) { if(arg < zero) return -1; else return 1; } static inline Result div(const Result &number, const T &divisor) { static_cast(static_check); if(number == zero && divisor == zero) return std::numeric_limits::quiet_NaN(); if(number == zero) return zero; if(divisor == zero) return sig(number) * std::numeric_limits::infinity(); assert(zero < number); assert(zero < divisor); // Avoid underflow if(static_cast(1) < abs(divisor)) { // number / divisor < min <=> number < min * divisor if( abs(number) < abs(divisor) * std::numeric_limits::min()) { return sig(divisor) * sig(number) * std::numeric_limits::min(); } } // Avoid overflow if( abs(divisor) < static_cast(1)) { // number / divisor > max <=> number > max * divisor if( abs(divisor) * std::numeric_limits::max() < abs(number)) { return sig(divisor) * sig(number) * std::numeric_limits::max(); } } return number / divisor; } }; template const typename fpt_traits::StdNumericLimitsNotAvailable fpt_traits::static_check[ std::numeric_limits::is_specialized ] = { {} }; template const T fpt_traits::zero = static_cast(0); template bool check_tolerance(T actual, T expected, U fraction) { typename fpt_traits::Result diff = fpt_traits::div( fpt_traits::abs( expected - actual ), fpt_traits::abs( expected ) ); return (diff == fraction) || (diff < fraction); } } // namespace detail template void ensure_close(const char* msg, const T& actual, const T& expected, const U& tolerance ) { typedef detail::fpt_traits Traits; typename Traits::Result fraction = Traits::div( Traits::abs(static_cast(tolerance)), static_cast(100) ); if( !detail::check_tolerance(actual, expected, fraction) ) { std::ostringstream ss; ss << ( msg ? msg : "" ) << ( msg ? ": " : "" ) << "expected `" << expected << "` and actual `" << actual << "` differ more than " << tolerance << "%"; throw failure( ss.str().c_str() ); } } template void ensure_close(const T& actual, const T& expected, const Tolerance& tolerance ) { ensure_close( 0, actual, expected, tolerance ); } template void ensure_close_fraction(const char* msg, const T& actual, const T& expected, const U& fraction) { typedef char StdNumericLimitsNotAvailable; const StdNumericLimitsNotAvailable static_check[ std::numeric_limits::is_specialized ] = { 0 }; static_cast(static_check); typedef typename detail::If::is_integer, double, U>::type Tolerance; if( !detail::check_tolerance(actual, expected, fraction) ) { std::ostringstream ss; ss << ( msg ? msg : "" ) << ( msg ? ": " : "" ) << "expected `" << expected << "` and actual `" << actual << "` differ more than fraction `" << fraction << "`"; throw failure( ss.str().c_str() ); } } template void ensure_close_fraction( const char* msg, const T& actual, const T& expected, const int& tolerance ) { ensure_close(msg, actual, expected, double(tolerance)); } template< typename T, typename Tolerance> void ensure_close_fraction(const T& actual, const T& expected, const Tolerance& fraction) { ensure_close_fraction( 0, actual, expected, fraction ); } } // namespace tut #endif