Chris@16: // (C) Copyright Gennadiy Rozental 2001-2008. Chris@16: // Distributed under the Boost Software License, Version 1.0. Chris@16: // (See accompanying file LICENSE_1_0.txt or copy at Chris@16: // http://www.boost.org/LICENSE_1_0.txt) Chris@16: Chris@16: // See http://www.boost.org/libs/test for the library home page. Chris@16: // Chris@16: // File : $RCSfile$ Chris@16: // Chris@101: // Version : $Revision$ Chris@16: // Chris@16: // Description : defines algoirthms for comparing 2 floating point values Chris@16: // *************************************************************************** Chris@16: Chris@16: #ifndef BOOST_TEST_FLOATING_POINT_COMPARISON_HPP_071894GER Chris@16: #define BOOST_TEST_FLOATING_POINT_COMPARISON_HPP_071894GER Chris@16: Chris@16: // Boost.Test Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: Chris@16: // Boost Chris@16: #include // for std::numeric_limits Chris@16: #include // for numeric::conversion_traits Chris@16: #include Chris@16: Chris@16: #include Chris@16: Chris@16: //____________________________________________________________________________// Chris@16: Chris@16: namespace boost { Chris@16: Chris@16: namespace test_tools { Chris@16: Chris@16: using unit_test::readonly_property; Chris@16: Chris@16: // ************************************************************************** // Chris@16: // ************** floating_point_comparison_type ************** // Chris@16: // ************************************************************************** // Chris@16: Chris@16: enum floating_point_comparison_type { Chris@16: FPC_STRONG, // "Very close" - equation 1' in docs, the default Chris@16: FPC_WEAK // "Close enough" - equation 2' in docs. Chris@16: Chris@16: }; Chris@16: Chris@16: // ************************************************************************** // Chris@16: // ************** details ************** // Chris@16: // ************************************************************************** // Chris@16: Chris@16: namespace tt_detail { Chris@16: Chris@16: // FPT is Floating-Point Type: float, double, long double or User-Defined. Chris@16: template Chris@16: inline FPT Chris@16: fpt_abs( FPT fpv ) Chris@16: { Chris@16: return fpv < static_cast(0) ? -fpv : fpv; Chris@16: } Chris@16: Chris@16: //____________________________________________________________________________// Chris@16: Chris@16: template Chris@16: struct fpt_limits { Chris@16: static FPT min_value() Chris@16: { Chris@16: return std::numeric_limits::is_specialized Chris@16: ? (std::numeric_limits::min)() Chris@16: : 0; Chris@16: } Chris@16: static FPT max_value() Chris@16: { Chris@16: return std::numeric_limits::is_specialized Chris@16: ? (std::numeric_limits::max)() Chris@16: : static_cast(1000000); // for the our purpuses it doesn't really matter what value is returned here Chris@16: } Chris@16: }; Chris@16: Chris@16: //____________________________________________________________________________// Chris@16: Chris@16: // both f1 and f2 are unsigned here Chris@16: template Chris@16: inline FPT Chris@16: safe_fpt_division( FPT f1, FPT f2 ) Chris@16: { Chris@16: // Avoid overflow. Chris@16: if( (f2 < static_cast(1)) && (f1 > f2*fpt_limits::max_value()) ) Chris@16: return fpt_limits::max_value(); Chris@16: Chris@16: // Avoid underflow. Chris@16: if( (f1 == static_cast(0)) || Chris@16: ((f2 > static_cast(1)) && (f1 < f2*fpt_limits::min_value())) ) Chris@16: return static_cast(0); Chris@16: Chris@16: return f1/f2; Chris@16: } Chris@16: Chris@16: //____________________________________________________________________________// Chris@16: Chris@16: } // namespace tt_detail Chris@16: Chris@16: // ************************************************************************** // Chris@16: // ************** tolerance presentation types ************** // Chris@16: // ************************************************************************** // Chris@16: Chris@16: template Chris@16: struct percent_tolerance_t { Chris@16: explicit percent_tolerance_t( FPT v ) : m_value( v ) {} Chris@16: Chris@16: FPT m_value; Chris@16: }; Chris@16: Chris@16: //____________________________________________________________________________// Chris@16: Chris@16: template Chris@16: Out& operator<<( Out& out, percent_tolerance_t t ) Chris@16: { Chris@16: return out << t.m_value; Chris@16: } Chris@16: Chris@16: //____________________________________________________________________________// Chris@16: Chris@16: template Chris@16: inline percent_tolerance_t Chris@16: percent_tolerance( FPT v ) Chris@16: { Chris@16: return percent_tolerance_t( v ); Chris@16: } Chris@16: Chris@16: //____________________________________________________________________________// Chris@16: Chris@16: template Chris@16: struct fraction_tolerance_t { Chris@16: explicit fraction_tolerance_t( FPT v ) : m_value( v ) {} Chris@16: Chris@16: FPT m_value; Chris@16: }; Chris@16: Chris@16: //____________________________________________________________________________// Chris@16: Chris@16: template Chris@16: Out& operator<<( Out& out, fraction_tolerance_t t ) Chris@16: { Chris@16: return out << t.m_value; Chris@16: } Chris@16: Chris@16: //____________________________________________________________________________// Chris@16: Chris@16: template Chris@16: inline fraction_tolerance_t Chris@16: fraction_tolerance( FPT v ) Chris@16: { Chris@16: return fraction_tolerance_t( v ); Chris@16: } Chris@16: Chris@16: //____________________________________________________________________________// Chris@16: Chris@16: // ************************************************************************** // Chris@16: // ************** close_at_tolerance ************** // Chris@16: // ************************************************************************** // Chris@16: Chris@16: template Chris@16: class close_at_tolerance { Chris@16: public: Chris@16: // Public typedefs Chris@16: typedef bool result_type; Chris@16: Chris@16: // Constructor Chris@16: template Chris@16: explicit close_at_tolerance( percent_tolerance_t tolerance, Chris@16: floating_point_comparison_type fpc_type = FPC_STRONG ) Chris@16: : p_fraction_tolerance( tt_detail::fpt_abs( static_cast(0.01)*tolerance.m_value ) ) Chris@16: , p_strong_or_weak( fpc_type == FPC_STRONG ) Chris@16: , m_report_modifier( 100. ) Chris@16: {} Chris@16: template Chris@16: explicit close_at_tolerance( fraction_tolerance_t tolerance, Chris@16: floating_point_comparison_type fpc_type = FPC_STRONG ) Chris@16: : p_fraction_tolerance( tt_detail::fpt_abs( tolerance.m_value ) ) Chris@16: , p_strong_or_weak( fpc_type == FPC_STRONG ) Chris@16: , m_report_modifier( 1. ) Chris@16: {} Chris@16: Chris@16: predicate_result operator()( FPT left, FPT right ) const Chris@16: { Chris@16: FPT diff = tt_detail::fpt_abs( left - right ); Chris@16: FPT d1 = tt_detail::safe_fpt_division( diff, tt_detail::fpt_abs( right ) ); Chris@16: FPT d2 = tt_detail::safe_fpt_division( diff, tt_detail::fpt_abs( left ) ); Chris@16: Chris@16: predicate_result res( p_strong_or_weak Chris@16: ? (d1 <= p_fraction_tolerance.get() && d2 <= p_fraction_tolerance.get()) Chris@16: : (d1 <= p_fraction_tolerance.get() || d2 <= p_fraction_tolerance.get()) ); Chris@16: Chris@16: if( !res ) Chris@16: res.message() << (( d1 <= p_fraction_tolerance.get() ? d2 : d1 ) * m_report_modifier); Chris@16: Chris@16: return res; Chris@16: } Chris@16: Chris@16: // Public properties Chris@16: readonly_property p_fraction_tolerance; Chris@16: readonly_property p_strong_or_weak; Chris@16: private: Chris@16: // Data members Chris@16: FPT m_report_modifier; Chris@16: }; Chris@16: Chris@16: //____________________________________________________________________________// Chris@16: Chris@16: // ************************************************************************** // Chris@16: // ************** check_is_close ************** // Chris@16: // ************************************************************************** // Chris@16: Chris@16: struct BOOST_TEST_DECL check_is_close_t { Chris@16: // Public typedefs Chris@16: typedef bool result_type; Chris@16: Chris@16: template Chris@16: predicate_result Chris@16: operator()( FPT1 left, FPT2 right, percent_tolerance_t tolerance, Chris@16: floating_point_comparison_type fpc_type = FPC_STRONG ) const Chris@16: { Chris@16: // deduce "better" type from types of arguments being compared Chris@16: // if one type is floating and the second integral we use floating type and Chris@16: // value of integral type is promoted to the floating. The same for float and double Chris@16: // But we don't want to compare two values of integral types using this tool. Chris@16: typedef typename numeric::conversion_traits::supertype FPT; Chris@16: BOOST_STATIC_ASSERT( !is_integral::value ); Chris@16: Chris@16: close_at_tolerance pred( tolerance, fpc_type ); Chris@16: Chris@16: return pred( left, right ); Chris@16: } Chris@16: template Chris@16: predicate_result Chris@16: operator()( FPT1 left, FPT2 right, fraction_tolerance_t tolerance, Chris@16: floating_point_comparison_type fpc_type = FPC_STRONG ) const Chris@16: { Chris@16: // same as in a comment above Chris@16: typedef typename numeric::conversion_traits::supertype FPT; Chris@16: BOOST_STATIC_ASSERT( !is_integral::value ); Chris@16: Chris@16: close_at_tolerance pred( tolerance, fpc_type ); Chris@16: Chris@16: return pred( left, right ); Chris@16: } Chris@16: }; Chris@16: Chris@16: namespace { Chris@16: check_is_close_t const& check_is_close = unit_test::ut_detail::static_constant::value; Chris@16: } Chris@16: Chris@16: //____________________________________________________________________________// Chris@16: Chris@16: // ************************************************************************** // Chris@16: // ************** check_is_small ************** // Chris@16: // ************************************************************************** // Chris@16: Chris@16: struct BOOST_TEST_DECL check_is_small_t { Chris@16: // Public typedefs Chris@16: typedef bool result_type; Chris@16: Chris@16: template Chris@16: bool Chris@16: operator()( FPT fpv, FPT tolerance ) const Chris@16: { Chris@16: return tt_detail::fpt_abs( fpv ) < tt_detail::fpt_abs( tolerance ); Chris@16: } Chris@16: }; Chris@16: Chris@16: namespace { Chris@16: check_is_small_t const& check_is_small = unit_test::ut_detail::static_constant::value; Chris@16: } Chris@16: Chris@16: //____________________________________________________________________________// Chris@16: Chris@16: } // namespace test_tools Chris@16: Chris@16: } // namespace boost Chris@16: Chris@16: //____________________________________________________________________________// Chris@16: Chris@16: #include Chris@16: Chris@16: #endif // BOOST_FLOATING_POINT_COMAPARISON_HPP_071894GER