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