Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 09:52:41

0001 //  (C) Copyright Gennadiy Rozental 2001.
0002 //  Distributed under the Boost Software License, Version 1.0.
0003 //  (See accompanying file LICENSE_1_0.txt or copy at 
0004 //  http://www.boost.org/LICENSE_1_0.txt)
0005 
0006 //  See http://www.boost.org/libs/test for the library home page.
0007 //
0008 //!@file 
0009 //!@brief algorithms for comparing floating point values
0010 // ***************************************************************************
0011 
0012 #ifndef BOOST_TEST_FLOATING_POINT_COMPARISON_HPP_071894GER
0013 #define BOOST_TEST_FLOATING_POINT_COMPARISON_HPP_071894GER
0014 
0015 // Boost.Test
0016 #include <boost/test/detail/global_typedef.hpp>
0017 #include <boost/test/tools/assertion_result.hpp>
0018 
0019 // Boost
0020 #include <boost/limits.hpp>  // for std::numeric_limits
0021 #include <boost/static_assert.hpp>
0022 #include <boost/assert.hpp>
0023 #include <boost/mpl/bool.hpp>
0024 #include <boost/type_traits/is_floating_point.hpp>
0025 #include <boost/type_traits/is_array.hpp>
0026 #include <boost/type_traits/is_reference.hpp>
0027 #include <boost/type_traits/is_void.hpp>
0028 #include <boost/type_traits/conditional.hpp>
0029 #include <boost/utility/enable_if.hpp>
0030 
0031 // STL
0032 #include <iosfwd>
0033 
0034 #include <boost/test/detail/suppress_warnings.hpp>
0035 
0036 //____________________________________________________________________________//
0037 
0038 namespace boost {
0039 namespace math {
0040 namespace fpc {
0041 
0042 // ************************************************************************** //
0043 // **************              fpc::tolerance_based            ************** //
0044 // ************************************************************************** //
0045 
0046 
0047 //! @internal
0048 //! Protects the instanciation of std::numeric_limits from non-supported types (eg. T=array)
0049 template <typename T, bool enabled>
0050 struct tolerance_based_delegate;
0051 
0052 template <typename T>
0053 struct tolerance_based_delegate<T, false> : mpl::false_ {};
0054 
0055 // from https://stackoverflow.com/a/16509511/1617295
0056 template<typename T>
0057 class is_abstract_class_or_function
0058 {
0059     typedef char (&Two)[2];
0060     template<typename U> static char test(U(*)[1]);
0061     template<typename U> static Two test(...);
0062 
0063 public:
0064     static const bool value =
0065            !is_reference<T>::value
0066         && !is_void<T>::value
0067         && (sizeof(test<T>(0)) == sizeof(Two));
0068 };
0069 
0070 // warning: we cannot instanciate std::numeric_limits for incomplete types, we use is_abstract_class_or_function
0071 // prior to the specialization below
0072 template <typename T>
0073 struct tolerance_based_delegate<T, true>
0074 : mpl::bool_<
0075     is_floating_point<T>::value ||
0076     (!std::numeric_limits<T>::is_integer && std::numeric_limits<T>::is_specialized && !std::numeric_limits<T>::is_exact)>
0077 {};
0078 
0079 
0080 /*!@brief Indicates if a type can be compared using a tolerance scheme
0081  *
0082  * This is a metafunction that should evaluate to @c mpl::true_ if the type
0083  * @c T can be compared using a tolerance based method, typically for floating point
0084  * types.
0085  *
0086  * This metafunction can be specialized further to declare user types that are
0087  * floating point (eg. boost.multiprecision).
0088  */
0089 template <typename T>
0090 struct tolerance_based : tolerance_based_delegate<T, !is_array<T>::value && !is_abstract_class_or_function<T>::value>::type {};
0091 
0092 // ************************************************************************** //
0093 // **************                 fpc::strength                ************** //
0094 // ************************************************************************** //
0095 
0096 //! Method for comparing floating point numbers
0097 enum strength {
0098     FPC_STRONG, //!< "Very close"   - equation 2' in docs, the default
0099     FPC_WEAK    //!< "Close enough" - equation 3' in docs.
0100 };
0101 
0102 
0103 // ************************************************************************** //
0104 // **************         tolerance presentation types         ************** //
0105 // ************************************************************************** //
0106 
0107 template<typename FPT>
0108 struct percent_tolerance_t {
0109     explicit    percent_tolerance_t( FPT v ) : m_value( v ) {}
0110 
0111     FPT m_value;
0112 };
0113 
0114 //____________________________________________________________________________//
0115 
0116 template<typename FPT>
0117 inline std::ostream& operator<<( std::ostream& out, percent_tolerance_t<FPT> t )
0118 {
0119     return out << t.m_value;
0120 }
0121 
0122 //____________________________________________________________________________//
0123 
0124 template<typename FPT>
0125 inline percent_tolerance_t<FPT>
0126 percent_tolerance( FPT v )
0127 {
0128     return percent_tolerance_t<FPT>( v );
0129 }
0130 
0131 //____________________________________________________________________________//
0132 
0133 // ************************************************************************** //
0134 // **************                    details                   ************** //
0135 // ************************************************************************** //
0136 
0137 namespace fpc_detail {
0138 
0139 // FPT is Floating-Point Type: float, double, long double or User-Defined.
0140 template<typename FPT>
0141 inline FPT
0142 fpt_abs( FPT fpv ) 
0143 {
0144     return fpv < static_cast<FPT>(0) ? -fpv : fpv;
0145 }
0146 
0147 //____________________________________________________________________________//
0148 
0149 template<typename FPT>
0150 struct fpt_specialized_limits
0151 {
0152   static FPT    min_value() { return (std::numeric_limits<FPT>::min)(); }
0153   static FPT    max_value() { return (std::numeric_limits<FPT>::max)(); }
0154 };
0155 
0156 template<typename FPT>
0157 struct fpt_non_specialized_limits
0158 {
0159   static FPT    min_value() { return static_cast<FPT>(0); }
0160   static FPT    max_value() { return static_cast<FPT>(1000000); } // for our purposes it doesn't really matter what value is returned here
0161 };
0162 
0163 template<typename FPT>
0164 struct fpt_limits : boost::conditional<std::numeric_limits<FPT>::is_specialized,
0165                                        fpt_specialized_limits<FPT>,
0166                                        fpt_non_specialized_limits<FPT>
0167                                       >::type
0168 {};
0169 
0170 //____________________________________________________________________________//
0171 
0172 // both f1 and f2 are unsigned here
0173 template<typename FPT>
0174 inline FPT
0175 safe_fpt_division( FPT f1, FPT f2 )
0176 {
0177     // Avoid overflow.
0178     if( (f2 < static_cast<FPT>(1))  && (f1 > f2*fpt_limits<FPT>::max_value()) )
0179         return fpt_limits<FPT>::max_value();
0180 
0181     // Avoid underflow.
0182     if( (fpt_abs(f1) <= fpt_limits<FPT>::min_value()) ||
0183         ((f2 > static_cast<FPT>(1)) && (f1 < f2*fpt_limits<FPT>::min_value())) )
0184         return static_cast<FPT>(0);
0185 
0186     return f1/f2;
0187 }
0188 
0189 //____________________________________________________________________________//
0190 
0191 template<typename FPT, typename ToleranceType>
0192 inline FPT
0193 fraction_tolerance( ToleranceType tolerance )
0194 {
0195   return static_cast<FPT>(tolerance);
0196 } 
0197 
0198 //____________________________________________________________________________//
0199 
0200 template<typename FPT2, typename FPT>
0201 inline FPT2
0202 fraction_tolerance( percent_tolerance_t<FPT> tolerance )
0203 {
0204     return FPT2(tolerance.m_value)*FPT2(0.01); 
0205 }
0206 
0207 //____________________________________________________________________________//
0208 
0209 } // namespace fpc_detail
0210 
0211 // ************************************************************************** //
0212 // **************             close_at_tolerance               ************** //
0213 // ************************************************************************** //
0214 
0215 
0216 /*!@brief Predicate for comparing floating point numbers
0217  *
0218  * This predicate is used to compare floating point numbers. In addition the comparison produces maximum 
0219  * related difference, which can be used to generate detailed error message
0220  * The methods for comparing floating points are detailed in the documentation. The method is chosen
0221  * by the @ref boost::math::fpc::strength given at construction.
0222  *
0223  * This predicate is not suitable for comparing to 0 or to infinity.
0224  */
0225 template<typename FPT>
0226 class close_at_tolerance {
0227 public:
0228     // Public typedefs
0229     typedef bool result_type;
0230 
0231     // Constructor
0232     template<typename ToleranceType>
0233     explicit    close_at_tolerance( ToleranceType tolerance, fpc::strength fpc_strength = FPC_STRONG ) 
0234     : m_fraction_tolerance( fpc_detail::fraction_tolerance<FPT>( tolerance ) )
0235     , m_strength( fpc_strength )
0236     , m_tested_rel_diff( 0 )
0237     {
0238         BOOST_ASSERT_MSG( m_fraction_tolerance >= FPT(0), "tolerance must not be negative!" ); // no reason for tolerance to be negative
0239     }
0240 
0241     // Access methods
0242     //! Returns the tolerance
0243     FPT                 fraction_tolerance() const  { return m_fraction_tolerance; }
0244 
0245     //! Returns the comparison method
0246     fpc::strength       strength() const            { return m_strength; }
0247 
0248     //! Returns the failing fraction
0249     FPT                 tested_rel_diff() const     { return m_tested_rel_diff; }
0250 
0251     /*! Compares two floating point numbers a and b such that their "left" relative difference |a-b|/a and/or
0252      * "right" relative difference |a-b|/b does not exceed specified relative (fraction) tolerance.
0253      *
0254      *  @param[in] left first floating point number to be compared
0255      *  @param[in] right second floating point number to be compared
0256      *
0257      * What is reported by @c tested_rel_diff in case of failure depends on the comparison method:
0258      * - for @c FPC_STRONG: the max of the two fractions
0259      * - for @c FPC_WEAK: the min of the two fractions
0260      * The rationale behind is to report the tolerance to set in order to make a test pass.
0261      */
0262     bool                operator()( FPT left, FPT right ) const
0263     {
0264         FPT diff              = fpc_detail::fpt_abs<FPT>( left - right );
0265         FPT fraction_of_right = fpc_detail::safe_fpt_division( diff, fpc_detail::fpt_abs( right ) );
0266         FPT fraction_of_left  = fpc_detail::safe_fpt_division( diff, fpc_detail::fpt_abs( left ) );
0267 
0268         FPT max_rel_diff = (std::max)( fraction_of_left, fraction_of_right );
0269         FPT min_rel_diff = (std::min)( fraction_of_left, fraction_of_right );
0270 
0271         m_tested_rel_diff = m_strength == FPC_STRONG ? max_rel_diff : min_rel_diff;
0272 
0273         return m_tested_rel_diff <= m_fraction_tolerance;
0274     }
0275 
0276 private:
0277     // Data members
0278     FPT                 m_fraction_tolerance;
0279     fpc::strength       m_strength;
0280     mutable FPT         m_tested_rel_diff;
0281 };
0282 
0283 // ************************************************************************** //
0284 // **************            small_with_tolerance              ************** //
0285 // ************************************************************************** //
0286 
0287 
0288 /*!@brief Predicate for comparing floating point numbers against 0
0289  *
0290  * Serves the same purpose as boost::math::fpc::close_at_tolerance, but used when one
0291  * of the operand is null. 
0292  */
0293 template<typename FPT>
0294 class small_with_tolerance {
0295 public:
0296     // Public typedefs
0297     typedef bool result_type;
0298 
0299     // Constructor
0300     explicit    small_with_tolerance( FPT tolerance ) // <= absolute tolerance
0301     : m_tolerance( tolerance )
0302     {
0303         BOOST_ASSERT( m_tolerance >= FPT(0) ); // no reason for the tolerance to be negative
0304     }
0305 
0306     // Action method
0307     bool        operator()( FPT fpv ) const
0308     {
0309         return fpc::fpc_detail::fpt_abs( fpv ) <= m_tolerance;
0310     }
0311 
0312 private:
0313     // Data members
0314     FPT         m_tolerance;
0315 };
0316 
0317 // ************************************************************************** //
0318 // **************                  is_small                    ************** //
0319 // ************************************************************************** //
0320 
0321 template<typename FPT>
0322 inline bool
0323 is_small( FPT fpv, FPT tolerance )
0324 {
0325     return small_with_tolerance<FPT>( tolerance )( fpv );
0326 }
0327 
0328 //____________________________________________________________________________//
0329 
0330 } // namespace fpc
0331 } // namespace math
0332 } // namespace boost
0333 
0334 #include <boost/test/detail/enable_warnings.hpp>
0335 
0336 #endif // BOOST_FLOATING_POINT_COMAPARISON_HPP_071894GER