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