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