/** * @brief Additional ensures for scientific/engineering applications. * @author Joerg <yogi2005@users.sourceforge.net> * @date 07/04/2008 */ #ifndef TUT_Float_H_GUARD #define TUT_Float_H_GUARD #include <limits> #include <iostream> namespace tut { namespace detail { template<bool Predicate, typename Then, typename Else> struct If { typedef Else type; }; template<typename Then, typename Else> struct If<true,Then,Else> { typedef Then type; }; template<typename T> struct fpt_traits { struct StdNumericLimitsNotAvailable {}; static const StdNumericLimitsNotAvailable static_check[ std::numeric_limits<T>::is_specialized ]; static const T zero; typedef typename If<std::numeric_limits<T>::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<void>(static_check); if(number == zero && divisor == zero) return std::numeric_limits<Result>::quiet_NaN(); if(number == zero) return zero; if(divisor == zero) return sig(number) * std::numeric_limits<Result>::infinity(); assert(zero < number); assert(zero < divisor); // Avoid underflow if(static_cast<T>(1) < abs(divisor)) { // number / divisor < min <=> number < min * divisor if( abs(number) < abs(divisor) * std::numeric_limits<T>::min()) { return sig(divisor) * sig(number) * std::numeric_limits<T>::min(); } } // Avoid overflow if( abs(divisor) < static_cast<T>(1)) { // number / divisor > max <=> number > max * divisor if( abs(divisor) * std::numeric_limits<T>::max() < abs(number)) { return sig(divisor) * sig(number) * std::numeric_limits<T>::max(); } } return number / divisor; } }; template<typename T> const typename fpt_traits<T>::StdNumericLimitsNotAvailable fpt_traits<T>::static_check[ std::numeric_limits<T>::is_specialized ] = { {} }; template<typename T> const T fpt_traits<T>::zero = static_cast<T>(0); template<typename T, typename U> bool check_tolerance(T actual, T expected, U fraction) { typename fpt_traits<T>::Result diff = fpt_traits<T>::div( fpt_traits<T>::abs( expected - actual ), fpt_traits<T>::abs( expected ) ); return (diff == fraction) || (diff < fraction); } } // namespace detail template<typename T, typename U> void ensure_close(const char* msg, const T& actual, const T& expected, const U& tolerance ) { typedef detail::fpt_traits<U> Traits; typename Traits::Result fraction = Traits::div( Traits::abs(static_cast<typename Traits::Result>(tolerance)), static_cast<typename Traits::Result>(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<typename T, typename Tolerance> void ensure_close(const T& actual, const T& expected, const Tolerance& tolerance ) { ensure_close( 0, actual, expected, tolerance ); } template<typename T, typename U> 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<U>::is_specialized ] = { 0 }; static_cast<void>(static_check); typedef typename detail::If<std::numeric_limits<U>::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<typename T> 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