floating_point_comparison.hpp 11 KB

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