316 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
		
		
			
		
	
	
			316 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
|   | //  (C) Copyright Gennadiy Rozental 2001. | ||
|  | //  Distributed under the Boost Software License, Version 1.0. | ||
|  | //  (See accompanying file LICENSE_1_0.txt or copy at  | ||
|  | //  http://www.boost.org/LICENSE_1_0.txt) | ||
|  | 
 | ||
|  | //  See http://www.boost.org/libs/test for the library home page. | ||
|  | // | ||
|  | //!@file  | ||
|  | //!@brief algorithms for comparing floating point values | ||
|  | // *************************************************************************** | ||
|  | 
 | ||
|  | #ifndef BOOST_TEST_FLOATING_POINT_COMPARISON_HPP_071894GER | ||
|  | #define BOOST_TEST_FLOATING_POINT_COMPARISON_HPP_071894GER | ||
|  | 
 | ||
|  | // Boost.Test | ||
|  | #include <boost/test/detail/global_typedef.hpp> | ||
|  | #include <boost/test/tools/assertion_result.hpp> | ||
|  | 
 | ||
|  | // Boost | ||
|  | #include <boost/limits.hpp>  // for std::numeric_limits | ||
|  | #include <boost/static_assert.hpp> | ||
|  | #include <boost/assert.hpp> | ||
|  | #include <boost/mpl/bool.hpp> | ||
|  | #include <boost/type_traits/is_floating_point.hpp> | ||
|  | #include <boost/type_traits/is_array.hpp> | ||
|  | #include <boost/type_traits/conditional.hpp> | ||
|  | #include <boost/utility/enable_if.hpp> | ||
|  | 
 | ||
|  | // STL | ||
|  | #include <iosfwd> | ||
|  | 
 | ||
|  | #include <boost/test/detail/suppress_warnings.hpp> | ||
|  | 
 | ||
|  | //____________________________________________________________________________// | ||
|  | 
 | ||
|  | namespace boost { | ||
|  | namespace math { | ||
|  | namespace fpc { | ||
|  | 
 | ||
|  | // ************************************************************************** // | ||
|  | // **************              fpc::tolerance_based            ************** // | ||
|  | // ************************************************************************** // | ||
|  | 
 | ||
|  | 
 | ||
|  | //! @internal | ||
|  | //! Protects the instanciation of std::numeric_limits from non-supported types (eg. T=array) | ||
|  | template <typename T, bool enabled> | ||
|  | struct tolerance_based_delegate; | ||
|  | 
 | ||
|  | template <typename T> | ||
|  | struct tolerance_based_delegate<T, false> : mpl::false_ {}; | ||
|  | 
 | ||
|  | template <typename T> | ||
|  | struct tolerance_based_delegate<T, true> | ||
|  | : mpl::bool_< | ||
|  |     is_floating_point<T>::value || | ||
|  |     (!std::numeric_limits<T>::is_integer && std::numeric_limits<T>::is_specialized && !std::numeric_limits<T>::is_exact)> | ||
|  | {}; | ||
|  | 
 | ||
|  | 
 | ||
|  | /*!@brief Indicates if a type can be compared using a tolerance scheme | ||
|  |  * | ||
|  |  * This is a metafunction that should evaluate to @c mpl::true_ if the type | ||
|  |  * @c T can be compared using a tolerance based method, typically for floating point | ||
|  |  * types. | ||
|  |  * | ||
|  |  * This metafunction can be specialized further to declare user types that are | ||
|  |  * floating point (eg. boost.multiprecision). | ||
|  |  */ | ||
|  | template <typename T> | ||
|  | struct tolerance_based : tolerance_based_delegate<T, !is_array<T>::value >::type {}; | ||
|  | 
 | ||
|  | // ************************************************************************** // | ||
|  | // **************                 fpc::strength                ************** // | ||
|  | // ************************************************************************** // | ||
|  | 
 | ||
|  | //! Method for comparing floating point numbers | ||
|  | enum strength { | ||
|  |     FPC_STRONG, //!< "Very close"   - equation 2' in docs, the default | ||
|  |     FPC_WEAK    //!< "Close enough" - equation 3' in docs. | ||
|  | }; | ||
|  | 
 | ||
|  | 
 | ||
|  | // ************************************************************************** // | ||
|  | // **************         tolerance presentation types         ************** // | ||
|  | // ************************************************************************** // | ||
|  | 
 | ||
|  | template<typename FPT> | ||
|  | struct percent_tolerance_t { | ||
|  |     explicit    percent_tolerance_t( FPT v ) : m_value( v ) {} | ||
|  | 
 | ||
|  |     FPT m_value; | ||
|  | }; | ||
|  | 
 | ||
|  | //____________________________________________________________________________// | ||
|  | 
 | ||
|  | template<typename FPT> | ||
|  | inline std::ostream& operator<<( std::ostream& out, percent_tolerance_t<FPT> t ) | ||
|  | { | ||
|  |     return out << t.m_value; | ||
|  | } | ||
|  | 
 | ||
|  | //____________________________________________________________________________// | ||
|  | 
 | ||
|  | template<typename FPT> | ||
|  | inline percent_tolerance_t<FPT> | ||
|  | percent_tolerance( FPT v ) | ||
|  | { | ||
|  |     return percent_tolerance_t<FPT>( v ); | ||
|  | } | ||
|  | 
 | ||
|  | //____________________________________________________________________________// | ||
|  | 
 | ||
|  | // ************************************************************************** // | ||
|  | // **************                    details                   ************** // | ||
|  | // ************************************************************************** // | ||
|  | 
 | ||
|  | namespace fpc_detail { | ||
|  | 
 | ||
|  | // FPT is Floating-Point Type: float, double, long double or User-Defined. | ||
|  | template<typename FPT> | ||
|  | inline FPT | ||
|  | fpt_abs( FPT fpv )  | ||
|  | { | ||
|  |     return fpv < static_cast<FPT>(0) ? -fpv : fpv; | ||
|  | } | ||
|  | 
 | ||
|  | //____________________________________________________________________________// | ||
|  | 
 | ||
|  | template<typename FPT> | ||
|  | struct fpt_specialized_limits | ||
|  | { | ||
|  |   static FPT    min_value() { return (std::numeric_limits<FPT>::min)(); } | ||
|  |   static FPT    max_value() { return (std::numeric_limits<FPT>::max)(); } | ||
|  | }; | ||
|  | 
 | ||
|  | template<typename FPT> | ||
|  | struct fpt_non_specialized_limits | ||
|  | { | ||
|  |   static FPT    min_value() { return static_cast<FPT>(0); } | ||
|  |   static FPT    max_value() { return static_cast<FPT>(1000000); } // for our purposes it doesn't really matter what value is returned here | ||
|  | }; | ||
|  | 
 | ||
|  | template<typename FPT> | ||
|  | struct fpt_limits : boost::conditional<std::numeric_limits<FPT>::is_specialized, | ||
|  |                                        fpt_specialized_limits<FPT>, | ||
|  |                                        fpt_non_specialized_limits<FPT> | ||
|  |                                       >::type | ||
|  | {}; | ||
|  | 
 | ||
|  | //____________________________________________________________________________// | ||
|  | 
 | ||
|  | // both f1 and f2 are unsigned here | ||
|  | template<typename FPT> | ||
|  | inline FPT | ||
|  | safe_fpt_division( FPT f1, FPT f2 ) | ||
|  | { | ||
|  |     // Avoid overflow. | ||
|  |     if( (f2 < static_cast<FPT>(1))  && (f1 > f2*fpt_limits<FPT>::max_value()) ) | ||
|  |         return fpt_limits<FPT>::max_value(); | ||
|  | 
 | ||
|  |     // Avoid underflow. | ||
|  |     if( (f1 == static_cast<FPT>(0)) || | ||
|  |         ((f2 > static_cast<FPT>(1)) && (f1 < f2*fpt_limits<FPT>::min_value())) ) | ||
|  |         return static_cast<FPT>(0); | ||
|  | 
 | ||
|  |     return f1/f2; | ||
|  | } | ||
|  | 
 | ||
|  | //____________________________________________________________________________// | ||
|  | 
 | ||
|  | template<typename FPT, typename ToleranceType> | ||
|  | inline FPT | ||
|  | fraction_tolerance( ToleranceType tolerance ) | ||
|  | { | ||
|  |   return static_cast<FPT>(tolerance); | ||
|  | }  | ||
|  | 
 | ||
|  | //____________________________________________________________________________// | ||
|  | 
 | ||
|  | template<typename FPT2, typename FPT> | ||
|  | inline FPT2 | ||
|  | fraction_tolerance( percent_tolerance_t<FPT> tolerance ) | ||
|  | { | ||
|  |     return FPT2(tolerance.m_value)*FPT2(0.01);  | ||
|  | } | ||
|  | 
 | ||
|  | //____________________________________________________________________________// | ||
|  | 
 | ||
|  | } // namespace fpc_detail | ||
|  | 
 | ||
|  | // ************************************************************************** // | ||
|  | // **************             close_at_tolerance               ************** // | ||
|  | // ************************************************************************** // | ||
|  | 
 | ||
|  | 
 | ||
|  | /*!@brief Predicate for comparing floating point numbers | ||
|  |  * | ||
|  |  * This predicate is used to compare floating point numbers. In addition the comparison produces maximum  | ||
|  |  * related differnce, which can be used to generate detailed error message | ||
|  |  * The methods for comparing floating points are detailed in the documentation. The method is chosen | ||
|  |  * by the @ref boost::math::fpc::strength given at construction. | ||
|  |  */ | ||
|  | template<typename FPT> | ||
|  | class close_at_tolerance { | ||
|  | public: | ||
|  |     // Public typedefs | ||
|  |     typedef bool result_type; | ||
|  | 
 | ||
|  |     // Constructor | ||
|  |     template<typename ToleranceType> | ||
|  |     explicit    close_at_tolerance( ToleranceType tolerance, fpc::strength fpc_strength = FPC_STRONG )  | ||
|  |     : m_fraction_tolerance( fpc_detail::fraction_tolerance<FPT>( tolerance ) ) | ||
|  |     , m_strength( fpc_strength ) | ||
|  |     , m_tested_rel_diff( 0 ) | ||
|  |     { | ||
|  |         BOOST_ASSERT_MSG( m_fraction_tolerance >= FPT(0), "tolerance must not be negative!" ); // no reason for tolerance to be negative | ||
|  |     } | ||
|  | 
 | ||
|  |     // Access methods | ||
|  |     //! Returns the tolerance | ||
|  |     FPT                 fraction_tolerance() const  { return m_fraction_tolerance; } | ||
|  | 
 | ||
|  |     //! Returns the comparison method | ||
|  |     fpc::strength       strength() const            { return m_strength; } | ||
|  | 
 | ||
|  |     //! Returns the failing fraction | ||
|  |     FPT                 tested_rel_diff() const     { return m_tested_rel_diff; } | ||
|  | 
 | ||
|  |     /*! Compares two floating point numbers a and b such that their "left" relative difference |a-b|/a and/or | ||
|  |      * "right" relative difference |a-b|/b does not exceed specified relative (fraction) tolerance. | ||
|  |      * | ||
|  |      *  @param[in] left first floating point number to be compared | ||
|  |      *  @param[in] right second floating point number to be compared | ||
|  |      * | ||
|  |      * What is reported by @c tested_rel_diff in case of failure depends on the comparison method: | ||
|  |      * - for @c FPC_STRONG: the max of the two fractions | ||
|  |      * - for @c FPC_WEAK: the min of the two fractions | ||
|  |      * The rationale behind is to report the tolerance to set in order to make a test pass. | ||
|  |      */ | ||
|  |     bool                operator()( FPT left, FPT right ) const | ||
|  |     { | ||
|  |         FPT diff              = fpc_detail::fpt_abs<FPT>( left - right ); | ||
|  |         FPT fraction_of_right = fpc_detail::safe_fpt_division( diff, fpc_detail::fpt_abs( right ) ); | ||
|  |         FPT fraction_of_left  = fpc_detail::safe_fpt_division( diff, fpc_detail::fpt_abs( left ) ); | ||
|  | 
 | ||
|  |         FPT max_rel_diff = (std::max)( fraction_of_left, fraction_of_right ); | ||
|  |         FPT min_rel_diff = (std::min)( fraction_of_left, fraction_of_right ); | ||
|  | 
 | ||
|  |         m_tested_rel_diff = m_strength == FPC_STRONG ? max_rel_diff : min_rel_diff; | ||
|  | 
 | ||
|  |         return m_tested_rel_diff <= m_fraction_tolerance; | ||
|  |     } | ||
|  | 
 | ||
|  | private: | ||
|  |     // Data members | ||
|  |     FPT                 m_fraction_tolerance; | ||
|  |     fpc::strength       m_strength; | ||
|  |     mutable FPT         m_tested_rel_diff; | ||
|  | }; | ||
|  | 
 | ||
|  | // ************************************************************************** // | ||
|  | // **************            small_with_tolerance              ************** // | ||
|  | // ************************************************************************** // | ||
|  | 
 | ||
|  | 
 | ||
|  | /*!@brief Predicate for comparing floating point numbers against 0 | ||
|  |  * | ||
|  |  * Serves the same purpose as boost::math::fpc::close_at_tolerance, but used when one | ||
|  |  * of the operand is null.  | ||
|  |  */ | ||
|  | template<typename FPT> | ||
|  | class small_with_tolerance { | ||
|  | public: | ||
|  |     // Public typedefs | ||
|  |     typedef bool result_type; | ||
|  | 
 | ||
|  |     // Constructor | ||
|  |     explicit    small_with_tolerance( FPT tolerance ) // <= absolute tolerance | ||
|  |     : m_tolerance( tolerance ) | ||
|  |     { | ||
|  |         BOOST_ASSERT( m_tolerance >= FPT(0) ); // no reason for the tolerance to be negative | ||
|  |     } | ||
|  | 
 | ||
|  |     // Action method | ||
|  |     bool        operator()( FPT fpv ) const | ||
|  |     { | ||
|  |         return fpc::fpc_detail::fpt_abs( fpv ) <= m_tolerance; | ||
|  |     } | ||
|  | 
 | ||
|  | private: | ||
|  |     // Data members | ||
|  |     FPT         m_tolerance; | ||
|  | }; | ||
|  | 
 | ||
|  | // ************************************************************************** // | ||
|  | // **************                  is_small                    ************** // | ||
|  | // ************************************************************************** // | ||
|  | 
 | ||
|  | template<typename FPT> | ||
|  | inline bool | ||
|  | is_small( FPT fpv, FPT tolerance ) | ||
|  | { | ||
|  |     return small_with_tolerance<FPT>( tolerance )( fpv ); | ||
|  | } | ||
|  | 
 | ||
|  | //____________________________________________________________________________// | ||
|  | 
 | ||
|  | } // namespace fpc | ||
|  | } // namespace math | ||
|  | } // namespace boost | ||
|  | 
 | ||
|  | #include <boost/test/detail/enable_warnings.hpp> | ||
|  | 
 | ||
|  | #endif // BOOST_FLOATING_POINT_COMAPARISON_HPP_071894GER |