fpc_op.hpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  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 Floating point comparison with enhanced reporting
  9. // ***************************************************************************
  10. #ifndef BOOST_TEST_TOOLS_FPC_OP_HPP_050915GER
  11. #define BOOST_TEST_TOOLS_FPC_OP_HPP_050915GER
  12. // Boost.Test
  13. #include <boost/test/tools/assertion.hpp>
  14. #include <boost/test/tools/floating_point_comparison.hpp>
  15. #include <boost/test/tools/fpc_tolerance.hpp>
  16. // Boost
  17. #include <boost/type_traits/common_type.hpp>
  18. #include <boost/type_traits/is_arithmetic.hpp>
  19. #include <boost/utility/enable_if.hpp>
  20. #include <boost/test/detail/suppress_warnings.hpp>
  21. //____________________________________________________________________________//
  22. namespace boost {
  23. namespace test_tools {
  24. namespace assertion {
  25. namespace op {
  26. // ************************************************************************** //
  27. // ************** fpctraits ************** //
  28. // ************************************************************************** //
  29. // set of floating point comparison traits per comparison OP
  30. template<typename OP>
  31. struct fpctraits {
  32. // indicate if we should perform the operation with a "logical OR"
  33. // with the "equality under tolerance".
  34. static const bool equality_logical_disjunction = true;
  35. };
  36. template <typename Lhs, typename Rhs>
  37. struct fpctraits<op::LT<Lhs,Rhs> > {
  38. static const bool equality_logical_disjunction = false;
  39. };
  40. template <typename Lhs, typename Rhs>
  41. struct fpctraits<op::GT<Lhs,Rhs> > {
  42. static const bool equality_logical_disjunction = false;
  43. };
  44. //____________________________________________________________________________//
  45. // ************************************************************************** //
  46. // ************** set of overloads to select correct fpc algo ************** //
  47. // ************************************************************************** //
  48. // we really only care about EQ vs NE. All other comparisons use direct first
  49. // and then need EQ. For example a <= b (tolerance t) IFF a <= b OR a == b (tolerance t)
  50. template <typename FPT, typename Lhs, typename Rhs, typename OP>
  51. inline assertion_result
  52. compare_fpv( Lhs const& lhs, Rhs const& rhs, OP* cmp_operator)
  53. {
  54. assertion_result result_direct_compare = cmp_operator->eval_direct(lhs, rhs);
  55. if(fpctraits<OP>::equality_logical_disjunction) {
  56. // this look like this can be simplified, but combining result && compare_fpv
  57. // looses the message in the return value of compare_fpv
  58. if( result_direct_compare ) {
  59. result_direct_compare.message() << "operation" << OP::forward() << "on arguments yields 'true'.";
  60. return result_direct_compare;
  61. }
  62. // result || compare_fpv(EQ)
  63. assertion_result result_eq = compare_fpv<FPT>(lhs, rhs, (op::EQ<Lhs, Rhs>*)0);
  64. result_direct_compare = result_direct_compare || result_eq;
  65. if( !result_eq ) {
  66. result_direct_compare.message() << "operation" << op::EQ<Lhs, Rhs>::forward() << "on arguments yields 'false': " << result_eq.message() << ".";
  67. }
  68. return result_direct_compare;
  69. }
  70. if( !result_direct_compare ) {
  71. result_direct_compare.message() << "operation" << OP::forward() << " on arguments yields 'false'.";
  72. return result_direct_compare;
  73. }
  74. // result && compare_fpv(NE)
  75. assertion_result result_neq = compare_fpv<FPT>(lhs, rhs, (op::NE<Lhs, Rhs>*)0);
  76. result_direct_compare = result_direct_compare && result_neq;
  77. if( !result_neq ) {
  78. result_direct_compare.message() << "operation" << op::NE<Lhs, Rhs>::forward() << "on arguments yields 'false': " << result_neq.message() << ".";
  79. }
  80. return result_direct_compare;
  81. }
  82. //____________________________________________________________________________//
  83. template <typename FPT, typename Lhs, typename Rhs>
  84. inline assertion_result
  85. compare_fpv_near_zero( FPT const& fpv, op::EQ<Lhs,Rhs>* )
  86. {
  87. fpc::small_with_tolerance<FPT> P( fpc_tolerance<FPT>() );
  88. assertion_result ar( P( fpv ) );
  89. if( !ar )
  90. ar.message() << "absolute value exceeds tolerance [|" << fpv << "| > "<< fpc_tolerance<FPT>() << ']';
  91. return ar;
  92. }
  93. //____________________________________________________________________________//
  94. template <typename FPT, typename Lhs, typename Rhs>
  95. inline assertion_result
  96. compare_fpv_near_zero( FPT const& fpv, op::NE<Lhs,Rhs>* )
  97. {
  98. fpc::small_with_tolerance<FPT> P( fpc_tolerance<FPT>() );
  99. assertion_result ar( !P( fpv ) );
  100. if( !ar )
  101. ar.message() << "absolute value is within tolerance [|" << fpv << "| < "<< fpc_tolerance<FPT>() << ']';
  102. return ar;
  103. }
  104. //____________________________________________________________________________//
  105. template <typename FPT, typename Lhs, typename Rhs>
  106. inline assertion_result
  107. compare_fpv( Lhs const& lhs, Rhs const& rhs, op::EQ<Lhs,Rhs>* )
  108. {
  109. if( lhs == 0 ) {
  110. return compare_fpv_near_zero<FPT>( rhs, (op::EQ<Lhs,Rhs>*)0 );
  111. }
  112. else if( rhs == 0) {
  113. return compare_fpv_near_zero<FPT>( lhs, (op::EQ<Lhs,Rhs>*)0 );
  114. }
  115. else {
  116. fpc::close_at_tolerance<FPT> P( fpc_tolerance<FPT>(), fpc::FPC_STRONG );
  117. assertion_result ar( P( lhs, rhs ) );
  118. if( !ar )
  119. ar.message() << "relative difference exceeds tolerance ["
  120. << P.tested_rel_diff() << " > " << P.fraction_tolerance() << ']';
  121. return ar;
  122. }
  123. }
  124. //____________________________________________________________________________//
  125. template <typename FPT, typename Lhs, typename Rhs>
  126. inline assertion_result
  127. compare_fpv( Lhs const& lhs, Rhs const& rhs, op::NE<Lhs,Rhs>* )
  128. {
  129. if( lhs == 0 ) {
  130. return compare_fpv_near_zero<FPT>( rhs, (op::NE<Lhs,Rhs>*)0 );
  131. }
  132. else if( rhs == 0 ) {
  133. return compare_fpv_near_zero<FPT>( lhs, (op::NE<Lhs,Rhs>*)0 );
  134. }
  135. else {
  136. fpc::close_at_tolerance<FPT> P( fpc_tolerance<FPT>(), fpc::FPC_WEAK );
  137. assertion_result ar( !P( lhs, rhs ) );
  138. if( !ar )
  139. ar.message() << "relative difference is within tolerance ["
  140. << P.tested_rel_diff() << " < " << fpc_tolerance<FPT>() << ']';
  141. return ar;
  142. }
  143. }
  144. //____________________________________________________________________________//
  145. #define DEFINE_FPV_COMPARISON( oper, name, rev, name_inverse ) \
  146. template<typename Lhs,typename Rhs> \
  147. struct name<Lhs,Rhs,typename boost::enable_if_c< \
  148. (fpc::tolerance_based<Lhs>::value && \
  149. fpc::tolerance_based<Rhs>::value) || \
  150. (fpc::tolerance_based<Lhs>::value && \
  151. boost::is_arithmetic<Rhs>::value) || \
  152. (boost::is_arithmetic<Lhs>::value && \
  153. fpc::tolerance_based<Rhs>::value) \
  154. >::type> { \
  155. public: \
  156. typedef typename common_type<Lhs,Rhs>::type FPT; \
  157. typedef name<Lhs,Rhs> OP; \
  158. typedef name_inverse<Lhs, Rhs> inverse; \
  159. \
  160. typedef assertion_result result_type; \
  161. \
  162. static bool \
  163. eval_direct( Lhs const& lhs, Rhs const& rhs ) \
  164. { \
  165. return lhs oper rhs; \
  166. } \
  167. \
  168. static assertion_result \
  169. eval( Lhs const& lhs, Rhs const& rhs ) \
  170. { \
  171. if( fpc_tolerance<FPT>() == FPT(0) \
  172. || (std::numeric_limits<Lhs>::has_infinity \
  173. && (lhs == std::numeric_limits<Lhs>::infinity())) \
  174. || (std::numeric_limits<Rhs>::has_infinity \
  175. && (rhs == std::numeric_limits<Rhs>::infinity()))) \
  176. { \
  177. return eval_direct( lhs, rhs ); \
  178. } \
  179. \
  180. return compare_fpv<FPT>( lhs, rhs, (OP*)0 ); \
  181. } \
  182. \
  183. template<typename PrevExprType> \
  184. static void \
  185. report( std::ostream& ostr, \
  186. PrevExprType const& lhs, \
  187. Rhs const& rhs ) \
  188. { \
  189. lhs.report( ostr ); \
  190. ostr << revert() \
  191. << tt_detail::print_helper( rhs ); \
  192. } \
  193. \
  194. static char const* forward() \
  195. { return " " #oper " "; } \
  196. static char const* revert() \
  197. { return " " #rev " "; } \
  198. }; \
  199. /**/
  200. BOOST_TEST_FOR_EACH_COMP_OP( DEFINE_FPV_COMPARISON )
  201. #undef DEFINE_FPV_COMPARISON
  202. //____________________________________________________________________________//
  203. } // namespace op
  204. } // namespace assertion
  205. } // namespace test_tools
  206. } // namespace boost
  207. #include <boost/test/detail/enable_warnings.hpp>
  208. #endif // BOOST_TEST_TOOLS_FPC_OP_HPP_050915GER