floating_point_comparison.hpp 11 KB

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