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

source code of include/boost/test/tools/floating_point_comparison.hpp