Chris@16
|
1 // (C) Copyright Gennadiy Rozental 2001-2008.
|
Chris@16
|
2 // Distributed under the Boost Software License, Version 1.0.
|
Chris@16
|
3 // (See accompanying file LICENSE_1_0.txt or copy at
|
Chris@16
|
4 // http://www.boost.org/LICENSE_1_0.txt)
|
Chris@16
|
5
|
Chris@16
|
6 // See http://www.boost.org/libs/test for the library home page.
|
Chris@16
|
7 //
|
Chris@16
|
8 // File : $RCSfile$
|
Chris@16
|
9 //
|
Chris@101
|
10 // Version : $Revision$
|
Chris@16
|
11 //
|
Chris@16
|
12 // Description : defines algoirthms for comparing 2 floating point values
|
Chris@16
|
13 // ***************************************************************************
|
Chris@16
|
14
|
Chris@16
|
15 #ifndef BOOST_TEST_FLOATING_POINT_COMPARISON_HPP_071894GER
|
Chris@16
|
16 #define BOOST_TEST_FLOATING_POINT_COMPARISON_HPP_071894GER
|
Chris@16
|
17
|
Chris@16
|
18 // Boost.Test
|
Chris@16
|
19 #include <boost/test/detail/global_typedef.hpp>
|
Chris@16
|
20 #include <boost/test/utils/class_properties.hpp>
|
Chris@16
|
21 #include <boost/test/predicate_result.hpp>
|
Chris@16
|
22
|
Chris@16
|
23 // Boost
|
Chris@16
|
24 #include <boost/limits.hpp> // for std::numeric_limits
|
Chris@16
|
25 #include <boost/numeric/conversion/conversion_traits.hpp> // for numeric::conversion_traits
|
Chris@16
|
26 #include <boost/static_assert.hpp>
|
Chris@16
|
27
|
Chris@16
|
28 #include <boost/test/detail/suppress_warnings.hpp>
|
Chris@16
|
29
|
Chris@16
|
30 //____________________________________________________________________________//
|
Chris@16
|
31
|
Chris@16
|
32 namespace boost {
|
Chris@16
|
33
|
Chris@16
|
34 namespace test_tools {
|
Chris@16
|
35
|
Chris@16
|
36 using unit_test::readonly_property;
|
Chris@16
|
37
|
Chris@16
|
38 // ************************************************************************** //
|
Chris@16
|
39 // ************** floating_point_comparison_type ************** //
|
Chris@16
|
40 // ************************************************************************** //
|
Chris@16
|
41
|
Chris@16
|
42 enum floating_point_comparison_type {
|
Chris@16
|
43 FPC_STRONG, // "Very close" - equation 1' in docs, the default
|
Chris@16
|
44 FPC_WEAK // "Close enough" - equation 2' in docs.
|
Chris@16
|
45
|
Chris@16
|
46 };
|
Chris@16
|
47
|
Chris@16
|
48 // ************************************************************************** //
|
Chris@16
|
49 // ************** details ************** //
|
Chris@16
|
50 // ************************************************************************** //
|
Chris@16
|
51
|
Chris@16
|
52 namespace tt_detail {
|
Chris@16
|
53
|
Chris@16
|
54 // FPT is Floating-Point Type: float, double, long double or User-Defined.
|
Chris@16
|
55 template<typename FPT>
|
Chris@16
|
56 inline FPT
|
Chris@16
|
57 fpt_abs( FPT fpv )
|
Chris@16
|
58 {
|
Chris@16
|
59 return fpv < static_cast<FPT>(0) ? -fpv : fpv;
|
Chris@16
|
60 }
|
Chris@16
|
61
|
Chris@16
|
62 //____________________________________________________________________________//
|
Chris@16
|
63
|
Chris@16
|
64 template<typename FPT>
|
Chris@16
|
65 struct fpt_limits {
|
Chris@16
|
66 static FPT min_value()
|
Chris@16
|
67 {
|
Chris@16
|
68 return std::numeric_limits<FPT>::is_specialized
|
Chris@16
|
69 ? (std::numeric_limits<FPT>::min)()
|
Chris@16
|
70 : 0;
|
Chris@16
|
71 }
|
Chris@16
|
72 static FPT max_value()
|
Chris@16
|
73 {
|
Chris@16
|
74 return std::numeric_limits<FPT>::is_specialized
|
Chris@16
|
75 ? (std::numeric_limits<FPT>::max)()
|
Chris@16
|
76 : static_cast<FPT>(1000000); // for the our purpuses it doesn't really matter what value is returned here
|
Chris@16
|
77 }
|
Chris@16
|
78 };
|
Chris@16
|
79
|
Chris@16
|
80 //____________________________________________________________________________//
|
Chris@16
|
81
|
Chris@16
|
82 // both f1 and f2 are unsigned here
|
Chris@16
|
83 template<typename FPT>
|
Chris@16
|
84 inline FPT
|
Chris@16
|
85 safe_fpt_division( FPT f1, FPT f2 )
|
Chris@16
|
86 {
|
Chris@16
|
87 // Avoid overflow.
|
Chris@16
|
88 if( (f2 < static_cast<FPT>(1)) && (f1 > f2*fpt_limits<FPT>::max_value()) )
|
Chris@16
|
89 return fpt_limits<FPT>::max_value();
|
Chris@16
|
90
|
Chris@16
|
91 // Avoid underflow.
|
Chris@16
|
92 if( (f1 == static_cast<FPT>(0)) ||
|
Chris@16
|
93 ((f2 > static_cast<FPT>(1)) && (f1 < f2*fpt_limits<FPT>::min_value())) )
|
Chris@16
|
94 return static_cast<FPT>(0);
|
Chris@16
|
95
|
Chris@16
|
96 return f1/f2;
|
Chris@16
|
97 }
|
Chris@16
|
98
|
Chris@16
|
99 //____________________________________________________________________________//
|
Chris@16
|
100
|
Chris@16
|
101 } // namespace tt_detail
|
Chris@16
|
102
|
Chris@16
|
103 // ************************************************************************** //
|
Chris@16
|
104 // ************** tolerance presentation types ************** //
|
Chris@16
|
105 // ************************************************************************** //
|
Chris@16
|
106
|
Chris@16
|
107 template<typename FPT>
|
Chris@16
|
108 struct percent_tolerance_t {
|
Chris@16
|
109 explicit percent_tolerance_t( FPT v ) : m_value( v ) {}
|
Chris@16
|
110
|
Chris@16
|
111 FPT m_value;
|
Chris@16
|
112 };
|
Chris@16
|
113
|
Chris@16
|
114 //____________________________________________________________________________//
|
Chris@16
|
115
|
Chris@16
|
116 template<typename Out,typename FPT>
|
Chris@16
|
117 Out& operator<<( Out& out, percent_tolerance_t<FPT> t )
|
Chris@16
|
118 {
|
Chris@16
|
119 return out << t.m_value;
|
Chris@16
|
120 }
|
Chris@16
|
121
|
Chris@16
|
122 //____________________________________________________________________________//
|
Chris@16
|
123
|
Chris@16
|
124 template<typename FPT>
|
Chris@16
|
125 inline percent_tolerance_t<FPT>
|
Chris@16
|
126 percent_tolerance( FPT v )
|
Chris@16
|
127 {
|
Chris@16
|
128 return percent_tolerance_t<FPT>( v );
|
Chris@16
|
129 }
|
Chris@16
|
130
|
Chris@16
|
131 //____________________________________________________________________________//
|
Chris@16
|
132
|
Chris@16
|
133 template<typename FPT>
|
Chris@16
|
134 struct fraction_tolerance_t {
|
Chris@16
|
135 explicit fraction_tolerance_t( FPT v ) : m_value( v ) {}
|
Chris@16
|
136
|
Chris@16
|
137 FPT m_value;
|
Chris@16
|
138 };
|
Chris@16
|
139
|
Chris@16
|
140 //____________________________________________________________________________//
|
Chris@16
|
141
|
Chris@16
|
142 template<typename Out,typename FPT>
|
Chris@16
|
143 Out& operator<<( Out& out, fraction_tolerance_t<FPT> t )
|
Chris@16
|
144 {
|
Chris@16
|
145 return out << t.m_value;
|
Chris@16
|
146 }
|
Chris@16
|
147
|
Chris@16
|
148 //____________________________________________________________________________//
|
Chris@16
|
149
|
Chris@16
|
150 template<typename FPT>
|
Chris@16
|
151 inline fraction_tolerance_t<FPT>
|
Chris@16
|
152 fraction_tolerance( FPT v )
|
Chris@16
|
153 {
|
Chris@16
|
154 return fraction_tolerance_t<FPT>( v );
|
Chris@16
|
155 }
|
Chris@16
|
156
|
Chris@16
|
157 //____________________________________________________________________________//
|
Chris@16
|
158
|
Chris@16
|
159 // ************************************************************************** //
|
Chris@16
|
160 // ************** close_at_tolerance ************** //
|
Chris@16
|
161 // ************************************************************************** //
|
Chris@16
|
162
|
Chris@16
|
163 template<typename FPT>
|
Chris@16
|
164 class close_at_tolerance {
|
Chris@16
|
165 public:
|
Chris@16
|
166 // Public typedefs
|
Chris@16
|
167 typedef bool result_type;
|
Chris@16
|
168
|
Chris@16
|
169 // Constructor
|
Chris@16
|
170 template<typename ToleranceBaseType>
|
Chris@16
|
171 explicit close_at_tolerance( percent_tolerance_t<ToleranceBaseType> tolerance,
|
Chris@16
|
172 floating_point_comparison_type fpc_type = FPC_STRONG )
|
Chris@16
|
173 : p_fraction_tolerance( tt_detail::fpt_abs( static_cast<FPT>(0.01)*tolerance.m_value ) )
|
Chris@16
|
174 , p_strong_or_weak( fpc_type == FPC_STRONG )
|
Chris@16
|
175 , m_report_modifier( 100. )
|
Chris@16
|
176 {}
|
Chris@16
|
177 template<typename ToleranceBaseType>
|
Chris@16
|
178 explicit close_at_tolerance( fraction_tolerance_t<ToleranceBaseType> tolerance,
|
Chris@16
|
179 floating_point_comparison_type fpc_type = FPC_STRONG )
|
Chris@16
|
180 : p_fraction_tolerance( tt_detail::fpt_abs( tolerance.m_value ) )
|
Chris@16
|
181 , p_strong_or_weak( fpc_type == FPC_STRONG )
|
Chris@16
|
182 , m_report_modifier( 1. )
|
Chris@16
|
183 {}
|
Chris@16
|
184
|
Chris@16
|
185 predicate_result operator()( FPT left, FPT right ) const
|
Chris@16
|
186 {
|
Chris@16
|
187 FPT diff = tt_detail::fpt_abs( left - right );
|
Chris@16
|
188 FPT d1 = tt_detail::safe_fpt_division( diff, tt_detail::fpt_abs( right ) );
|
Chris@16
|
189 FPT d2 = tt_detail::safe_fpt_division( diff, tt_detail::fpt_abs( left ) );
|
Chris@16
|
190
|
Chris@16
|
191 predicate_result res( p_strong_or_weak
|
Chris@16
|
192 ? (d1 <= p_fraction_tolerance.get() && d2 <= p_fraction_tolerance.get())
|
Chris@16
|
193 : (d1 <= p_fraction_tolerance.get() || d2 <= p_fraction_tolerance.get()) );
|
Chris@16
|
194
|
Chris@16
|
195 if( !res )
|
Chris@16
|
196 res.message() << (( d1 <= p_fraction_tolerance.get() ? d2 : d1 ) * m_report_modifier);
|
Chris@16
|
197
|
Chris@16
|
198 return res;
|
Chris@16
|
199 }
|
Chris@16
|
200
|
Chris@16
|
201 // Public properties
|
Chris@16
|
202 readonly_property<FPT> p_fraction_tolerance;
|
Chris@16
|
203 readonly_property<bool> p_strong_or_weak;
|
Chris@16
|
204 private:
|
Chris@16
|
205 // Data members
|
Chris@16
|
206 FPT m_report_modifier;
|
Chris@16
|
207 };
|
Chris@16
|
208
|
Chris@16
|
209 //____________________________________________________________________________//
|
Chris@16
|
210
|
Chris@16
|
211 // ************************************************************************** //
|
Chris@16
|
212 // ************** check_is_close ************** //
|
Chris@16
|
213 // ************************************************************************** //
|
Chris@16
|
214
|
Chris@16
|
215 struct BOOST_TEST_DECL check_is_close_t {
|
Chris@16
|
216 // Public typedefs
|
Chris@16
|
217 typedef bool result_type;
|
Chris@16
|
218
|
Chris@16
|
219 template<typename FPT1, typename FPT2, typename ToleranceBaseType>
|
Chris@16
|
220 predicate_result
|
Chris@16
|
221 operator()( FPT1 left, FPT2 right, percent_tolerance_t<ToleranceBaseType> tolerance,
|
Chris@16
|
222 floating_point_comparison_type fpc_type = FPC_STRONG ) const
|
Chris@16
|
223 {
|
Chris@16
|
224 // deduce "better" type from types of arguments being compared
|
Chris@16
|
225 // if one type is floating and the second integral we use floating type and
|
Chris@16
|
226 // value of integral type is promoted to the floating. The same for float and double
|
Chris@16
|
227 // But we don't want to compare two values of integral types using this tool.
|
Chris@16
|
228 typedef typename numeric::conversion_traits<FPT1,FPT2>::supertype FPT;
|
Chris@16
|
229 BOOST_STATIC_ASSERT( !is_integral<FPT>::value );
|
Chris@16
|
230
|
Chris@16
|
231 close_at_tolerance<FPT> pred( tolerance, fpc_type );
|
Chris@16
|
232
|
Chris@16
|
233 return pred( left, right );
|
Chris@16
|
234 }
|
Chris@16
|
235 template<typename FPT1, typename FPT2, typename ToleranceBaseType>
|
Chris@16
|
236 predicate_result
|
Chris@16
|
237 operator()( FPT1 left, FPT2 right, fraction_tolerance_t<ToleranceBaseType> tolerance,
|
Chris@16
|
238 floating_point_comparison_type fpc_type = FPC_STRONG ) const
|
Chris@16
|
239 {
|
Chris@16
|
240 // same as in a comment above
|
Chris@16
|
241 typedef typename numeric::conversion_traits<FPT1,FPT2>::supertype FPT;
|
Chris@16
|
242 BOOST_STATIC_ASSERT( !is_integral<FPT>::value );
|
Chris@16
|
243
|
Chris@16
|
244 close_at_tolerance<FPT> pred( tolerance, fpc_type );
|
Chris@16
|
245
|
Chris@16
|
246 return pred( left, right );
|
Chris@16
|
247 }
|
Chris@16
|
248 };
|
Chris@16
|
249
|
Chris@16
|
250 namespace {
|
Chris@16
|
251 check_is_close_t const& check_is_close = unit_test::ut_detail::static_constant<check_is_close_t>::value;
|
Chris@16
|
252 }
|
Chris@16
|
253
|
Chris@16
|
254 //____________________________________________________________________________//
|
Chris@16
|
255
|
Chris@16
|
256 // ************************************************************************** //
|
Chris@16
|
257 // ************** check_is_small ************** //
|
Chris@16
|
258 // ************************************************************************** //
|
Chris@16
|
259
|
Chris@16
|
260 struct BOOST_TEST_DECL check_is_small_t {
|
Chris@16
|
261 // Public typedefs
|
Chris@16
|
262 typedef bool result_type;
|
Chris@16
|
263
|
Chris@16
|
264 template<typename FPT>
|
Chris@16
|
265 bool
|
Chris@16
|
266 operator()( FPT fpv, FPT tolerance ) const
|
Chris@16
|
267 {
|
Chris@16
|
268 return tt_detail::fpt_abs( fpv ) < tt_detail::fpt_abs( tolerance );
|
Chris@16
|
269 }
|
Chris@16
|
270 };
|
Chris@16
|
271
|
Chris@16
|
272 namespace {
|
Chris@16
|
273 check_is_small_t const& check_is_small = unit_test::ut_detail::static_constant<check_is_small_t>::value;
|
Chris@16
|
274 }
|
Chris@16
|
275
|
Chris@16
|
276 //____________________________________________________________________________//
|
Chris@16
|
277
|
Chris@16
|
278 } // namespace test_tools
|
Chris@16
|
279
|
Chris@16
|
280 } // namespace boost
|
Chris@16
|
281
|
Chris@16
|
282 //____________________________________________________________________________//
|
Chris@16
|
283
|
Chris@16
|
284 #include <boost/test/detail/enable_warnings.hpp>
|
Chris@16
|
285
|
Chris@16
|
286 #endif // BOOST_FLOATING_POINT_COMAPARISON_HPP_071894GER
|