LCOV - code coverage report
Current view: top level - opt/homebrew/include/boost/test/tools - floating_point_comparison.hpp (source / functions) Hit Total Coverage
Test: test_dash_coverage.info Lines: 28 35 80.0 %
Date: 2026-06-25 07:23:51 Functions: 10 13 76.9 %

          Line data    Source code
       1             : //  (C) Copyright Gennadiy Rozental 2001.
       2             : //  Distributed under the Boost Software License, Version 1.0.
       3             : //  (See accompanying file LICENSE_1_0.txt or copy at 
       4             : //  http://www.boost.org/LICENSE_1_0.txt)
       5             : 
       6             : //  See http://www.boost.org/libs/test for the library home page.
       7             : //
       8             : //!@file 
       9             : //!@brief algorithms for comparing floating point values
      10             : // ***************************************************************************
      11             : 
      12             : #ifndef BOOST_TEST_FLOATING_POINT_COMPARISON_HPP_071894GER
      13             : #define BOOST_TEST_FLOATING_POINT_COMPARISON_HPP_071894GER
      14             : 
      15             : // Boost.Test
      16             : #include <boost/test/detail/global_typedef.hpp>
      17             : #include <boost/test/tools/assertion_result.hpp>
      18             : 
      19             : // Boost
      20             : #include <boost/limits.hpp>  // for std::numeric_limits
      21             : #include <boost/static_assert.hpp>
      22             : #include <boost/assert.hpp>
      23             : #include <boost/mpl/bool.hpp>
      24             : #include <boost/type_traits/is_floating_point.hpp>
      25             : #include <boost/type_traits/is_array.hpp>
      26             : #include <boost/type_traits/is_reference.hpp>
      27             : #include <boost/type_traits/is_void.hpp>
      28             : #include <boost/type_traits/conditional.hpp>
      29             : #include <boost/utility/enable_if.hpp>
      30             : 
      31             : // STL
      32             : #include <iosfwd>
      33             : 
      34             : #include <boost/test/detail/suppress_warnings.hpp>
      35             : 
      36             : //____________________________________________________________________________//
      37             : 
      38             : namespace boost {
      39             : namespace math {
      40             : namespace fpc {
      41             : 
      42             : // ************************************************************************** //
      43             : // **************              fpc::tolerance_based            ************** //
      44             : // ************************************************************************** //
      45             : 
      46             : 
      47             : //! @internal
      48             : //! Protects the instanciation of std::numeric_limits from non-supported types (eg. T=array)
      49             : template <typename T, bool enabled>
      50             : struct tolerance_based_delegate;
      51             : 
      52             : template <typename T>
      53             : struct tolerance_based_delegate<T, false> : mpl::false_ {};
      54             : 
      55             : // from https://stackoverflow.com/a/16509511/1617295
      56             : template<typename T>
      57             : class is_abstract_class_or_function
      58             : {
      59             :     typedef char (&Two)[2];
      60             :     template<typename U> static char test(U(*)[1]);
      61             :     template<typename U> static Two test(...);
      62             : 
      63             : public:
      64             :     static const bool value =
      65             :            !is_reference<T>::value
      66             :         && !is_void<T>::value
      67             :         && (sizeof(test<T>(0)) == sizeof(Two));
      68             : };
      69             : 
      70             : // warning: we cannot instanciate std::numeric_limits for incomplete types, we use is_abstract_class_or_function
      71             : // prior to the specialization below
      72             : template <typename T>
      73             : struct tolerance_based_delegate<T, true>
      74             : : mpl::bool_<
      75             :     is_floating_point<T>::value ||
      76             :     (!std::numeric_limits<T>::is_integer && std::numeric_limits<T>::is_specialized && !std::numeric_limits<T>::is_exact)>
      77             : {};
      78             : 
      79             : 
      80             : /*!@brief Indicates if a type can be compared using a tolerance scheme
      81             :  *
      82             :  * This is a metafunction that should evaluate to @c mpl::true_ if the type
      83             :  * @c T can be compared using a tolerance based method, typically for floating point
      84             :  * types.
      85             :  *
      86             :  * This metafunction can be specialized further to declare user types that are
      87             :  * floating point (eg. boost.multiprecision).
      88             :  */
      89             : template <typename T>
      90             : struct tolerance_based : tolerance_based_delegate<T, !is_array<T>::value && !is_abstract_class_or_function<T>::value>::type {};
      91             : 
      92             : // ************************************************************************** //
      93             : // **************                 fpc::strength                ************** //
      94             : // ************************************************************************** //
      95             : 
      96             : //! Method for comparing floating point numbers
      97             : enum strength {
      98             :     FPC_STRONG, //!< "Very close"   - equation 2' in docs, the default
      99             :     FPC_WEAK    //!< "Close enough" - equation 3' in docs.
     100             : };
     101             : 
     102             : 
     103             : // ************************************************************************** //
     104             : // **************         tolerance presentation types         ************** //
     105             : // ************************************************************************** //
     106             : 
     107             : template<typename FPT>
     108             : struct percent_tolerance_t {
     109           8 :     explicit    percent_tolerance_t( FPT v ) : m_value( v ) {}
     110             : 
     111             :     FPT m_value;
     112             : };
     113             : 
     114             : //____________________________________________________________________________//
     115             : 
     116             : template<typename FPT>
     117           0 : inline std::ostream& operator<<( std::ostream& out, percent_tolerance_t<FPT> t )
     118             : {
     119           0 :     return out << t.m_value;
     120             : }
     121             : 
     122             : //____________________________________________________________________________//
     123             : 
     124             : template<typename FPT>
     125             : inline percent_tolerance_t<FPT>
     126           4 : percent_tolerance( FPT v )
     127             : {
     128           4 :     return percent_tolerance_t<FPT>( v );
     129             : }
     130             : 
     131             : //____________________________________________________________________________//
     132             : 
     133             : // ************************************************************************** //
     134             : // **************                    details                   ************** //
     135             : // ************************************************************************** //
     136             : 
     137             : namespace fpc_detail {
     138             : 
     139             : // FPT is Floating-Point Type: float, double, long double or User-Defined.
     140             : template<typename FPT>
     141             : inline FPT
     142          20 : fpt_abs( FPT fpv ) 
     143             : {
     144          20 :     return fpv < static_cast<FPT>(0) ? -fpv : fpv;
     145             : }
     146             : 
     147             : //____________________________________________________________________________//
     148             : 
     149             : template<typename FPT>
     150             : struct fpt_specialized_limits
     151             : {
     152           8 :   static FPT    min_value() { return (std::numeric_limits<FPT>::min)(); }
     153           0 :   static FPT    max_value() { return (std::numeric_limits<FPT>::max)(); }
     154             : };
     155             : 
     156             : template<typename FPT>
     157             : struct fpt_non_specialized_limits
     158             : {
     159             :   static FPT    min_value() { return static_cast<FPT>(0); }
     160             :   static FPT    max_value() { return static_cast<FPT>(1000000); } // for our purposes it doesn't really matter what value is returned here
     161             : };
     162             : 
     163             : template<typename FPT>
     164             : struct fpt_limits : boost::conditional<std::numeric_limits<FPT>::is_specialized,
     165             :                                        fpt_specialized_limits<FPT>,
     166             :                                        fpt_non_specialized_limits<FPT>
     167             :                                       >::type
     168             : {};
     169             : 
     170             : //____________________________________________________________________________//
     171             : 
     172             : // both f1 and f2 are unsigned here
     173             : template<typename FPT>
     174             : inline FPT
     175           8 : safe_fpt_division( FPT f1, FPT f2 )
     176             : {
     177             :     // Avoid overflow.
     178           8 :     if( (f2 < static_cast<FPT>(1))  && (f1 > f2*fpt_limits<FPT>::max_value()) )
     179           0 :         return fpt_limits<FPT>::max_value();
     180             : 
     181             :     // Avoid underflow.
     182           8 :     if( (fpt_abs(f1) <= fpt_limits<FPT>::min_value()) ||
     183           0 :         ((f2 > static_cast<FPT>(1)) && (f1 < f2*fpt_limits<FPT>::min_value())) )
     184           8 :         return static_cast<FPT>(0);
     185             : 
     186           0 :     return f1/f2;
     187           8 : }
     188             : 
     189             : //____________________________________________________________________________//
     190             : 
     191             : template<typename FPT, typename ToleranceType>
     192             : inline FPT
     193             : fraction_tolerance( ToleranceType tolerance )
     194             : {
     195             :   return static_cast<FPT>(tolerance);
     196             : } 
     197             : 
     198             : //____________________________________________________________________________//
     199             : 
     200             : template<typename FPT2, typename FPT>
     201             : inline FPT2
     202           4 : fraction_tolerance( percent_tolerance_t<FPT> tolerance )
     203             : {
     204           4 :     return FPT2(tolerance.m_value)*FPT2(0.01); 
     205             : }
     206             : 
     207             : //____________________________________________________________________________//
     208             : 
     209             : } // namespace fpc_detail
     210             : 
     211             : // ************************************************************************** //
     212             : // **************             close_at_tolerance               ************** //
     213             : // ************************************************************************** //
     214             : 
     215             : 
     216             : /*!@brief Predicate for comparing floating point numbers
     217             :  *
     218             :  * This predicate is used to compare floating point numbers. In addition the comparison produces maximum 
     219             :  * related difference, which can be used to generate detailed error message
     220             :  * The methods for comparing floating points are detailed in the documentation. The method is chosen
     221             :  * by the @ref boost::math::fpc::strength given at construction.
     222             :  *
     223             :  * This predicate is not suitable for comparing to 0 or to infinity.
     224             :  */
     225             : template<typename FPT>
     226             : class close_at_tolerance {
     227             : public:
     228             :     // Public typedefs
     229             :     typedef bool result_type;
     230             : 
     231             :     // Constructor
     232             :     template<typename ToleranceType>
     233           8 :     explicit    close_at_tolerance( ToleranceType tolerance, fpc::strength fpc_strength = FPC_STRONG ) 
     234           4 :     : m_fraction_tolerance( fpc_detail::fraction_tolerance<FPT>( tolerance ) )
     235           4 :     , m_strength( fpc_strength )
     236           4 :     , m_tested_rel_diff( 0 )
     237           4 :     {
     238           8 :         BOOST_ASSERT_MSG( m_fraction_tolerance >= FPT(0), "tolerance must not be negative!" ); // no reason for tolerance to be negative
     239           8 :     }
     240             : 
     241             :     // Access methods
     242             :     //! Returns the tolerance
     243             :     FPT                 fraction_tolerance() const  { return m_fraction_tolerance; }
     244             : 
     245             :     //! Returns the comparison method
     246             :     fpc::strength       strength() const            { return m_strength; }
     247             : 
     248             :     //! Returns the failing fraction
     249           0 :     FPT                 tested_rel_diff() const     { return m_tested_rel_diff; }
     250             : 
     251             :     /*! Compares two floating point numbers a and b such that their "left" relative difference |a-b|/a and/or
     252             :      * "right" relative difference |a-b|/b does not exceed specified relative (fraction) tolerance.
     253             :      *
     254             :      *  @param[in] left first floating point number to be compared
     255             :      *  @param[in] right second floating point number to be compared
     256             :      *
     257             :      * What is reported by @c tested_rel_diff in case of failure depends on the comparison method:
     258             :      * - for @c FPC_STRONG: the max of the two fractions
     259             :      * - for @c FPC_WEAK: the min of the two fractions
     260             :      * The rationale behind is to report the tolerance to set in order to make a test pass.
     261             :      */
     262           4 :     bool                operator()( FPT left, FPT right ) const
     263             :     {
     264           4 :         FPT diff              = fpc_detail::fpt_abs<FPT>( left - right );
     265           4 :         FPT fraction_of_right = fpc_detail::safe_fpt_division( diff, fpc_detail::fpt_abs( right ) );
     266           4 :         FPT fraction_of_left  = fpc_detail::safe_fpt_division( diff, fpc_detail::fpt_abs( left ) );
     267             : 
     268           4 :         FPT max_rel_diff = (std::max)( fraction_of_left, fraction_of_right );
     269           4 :         FPT min_rel_diff = (std::min)( fraction_of_left, fraction_of_right );
     270             : 
     271           4 :         m_tested_rel_diff = m_strength == FPC_STRONG ? max_rel_diff : min_rel_diff;
     272             : 
     273           4 :         return m_tested_rel_diff <= m_fraction_tolerance;
     274             :     }
     275             : 
     276             : private:
     277             :     // Data members
     278             :     FPT                 m_fraction_tolerance;
     279             :     fpc::strength       m_strength;
     280             :     mutable FPT         m_tested_rel_diff;
     281             : };
     282             : 
     283             : // ************************************************************************** //
     284             : // **************            small_with_tolerance              ************** //
     285             : // ************************************************************************** //
     286             : 
     287             : 
     288             : /*!@brief Predicate for comparing floating point numbers against 0
     289             :  *
     290             :  * Serves the same purpose as boost::math::fpc::close_at_tolerance, but used when one
     291             :  * of the operand is null. 
     292             :  */
     293             : template<typename FPT>
     294             : class small_with_tolerance {
     295             : public:
     296             :     // Public typedefs
     297             :     typedef bool result_type;
     298             : 
     299             :     // Constructor
     300             :     explicit    small_with_tolerance( FPT tolerance ) // <= absolute tolerance
     301             :     : m_tolerance( tolerance )
     302             :     {
     303             :         BOOST_ASSERT( m_tolerance >= FPT(0) ); // no reason for the tolerance to be negative
     304             :     }
     305             : 
     306             :     // Action method
     307             :     bool        operator()( FPT fpv ) const
     308             :     {
     309             :         return fpc::fpc_detail::fpt_abs( fpv ) <= m_tolerance;
     310             :     }
     311             : 
     312             : private:
     313             :     // Data members
     314             :     FPT         m_tolerance;
     315             : };
     316             : 
     317             : // ************************************************************************** //
     318             : // **************                  is_small                    ************** //
     319             : // ************************************************************************** //
     320             : 
     321             : template<typename FPT>
     322             : inline bool
     323             : is_small( FPT fpv, FPT tolerance )
     324             : {
     325             :     return small_with_tolerance<FPT>( tolerance )( fpv );
     326             : }
     327             : 
     328             : //____________________________________________________________________________//
     329             : 
     330             : } // namespace fpc
     331             : } // namespace math
     332             : } // namespace boost
     333             : 
     334             : #include <boost/test/detail/enable_warnings.hpp>
     335             : 
     336             : #endif // BOOST_FLOATING_POINT_COMAPARISON_HPP_071894GER

Generated by: LCOV version 1.16