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 
38 namespace boost {
39 namespace math {
40 namespace 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)
49 template <typename T, bool enabled>
50 struct tolerance_based_delegate;
51 
52 template <typename T>
53 struct tolerance_based_delegate<T, false> : mpl::false_ {};
54 
55 // from https://stackoverflow.com/a/16509511/1617295
56 template<typename T>
57 class 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 
63 public:
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
72 template <typename T>
73 struct 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  */
89 template <typename T>
90 struct 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
97 enum 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 
107 template<typename FPT>
108 struct percent_tolerance_t {
percent_tolerance_tboost::math::fpc::percent_tolerance_t109     explicit    percent_tolerance_t( FPT v ) : m_value( v ) {}
110 
111     FPT m_value;
112 };
113 
114 //____________________________________________________________________________//
115 
116 template<typename FPT>
operator <<(std::ostream & out,percent_tolerance_t<FPT> t)117 inline std::ostream& operator<<( std::ostream& 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 // ************************************************************************** //
134 // **************                    details                   ************** //
135 // ************************************************************************** //
136 
137 namespace fpc_detail {
138 
139 // FPT is Floating-Point Type: float, double, long double or User-Defined.
140 template<typename FPT>
141 inline FPT
fpt_abs(FPT fpv)142 fpt_abs( FPT fpv )
143 {
144     return fpv < static_cast<FPT>(0) ? -fpv : fpv;
145 }
146 
147 //____________________________________________________________________________//
148 
149 template<typename FPT>
150 struct fpt_specialized_limits
151 {
min_valueboost::math::fpc::fpc_detail::fpt_specialized_limits152   static FPT    min_value() { return (std::numeric_limits<FPT>::min)(); }
max_valueboost::math::fpc::fpc_detail::fpt_specialized_limits153   static FPT    max_value() { return (std::numeric_limits<FPT>::max)(); }
154 };
155 
156 template<typename FPT>
157 struct fpt_non_specialized_limits
158 {
min_valueboost::math::fpc::fpc_detail::fpt_non_specialized_limits159   static FPT    min_value() { return static_cast<FPT>(0); }
max_valueboost::math::fpc::fpc_detail::fpt_non_specialized_limits160   static FPT    max_value() { return static_cast<FPT>(1000000); } // for our purposes it doesn't really matter what value is returned here
161 };
162 
163 template<typename FPT>
164 struct 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
173 template<typename FPT>
174 inline FPT
safe_fpt_division(FPT f1,FPT f2)175 safe_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 
191 template<typename FPT, typename ToleranceType>
192 inline FPT
fraction_tolerance(ToleranceType tolerance)193 fraction_tolerance( ToleranceType tolerance )
194 {
195   return static_cast<FPT>(tolerance);
196 }
197 
198 //____________________________________________________________________________//
199 
200 template<typename FPT2, typename FPT>
201 inline FPT2
fraction_tolerance(percent_tolerance_t<FPT> tolerance)202 fraction_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  */
225 template<typename FPT>
226 class close_at_tolerance {
227 public:
228     // Public typedefs
229     typedef bool result_type;
230 
231     // Constructor
232     template<typename ToleranceType>
close_at_tolerance(ToleranceType tolerance,fpc::strength fpc_strength=FPC_STRONG)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
fraction_tolerance() const243     FPT                 fraction_tolerance() const  { return m_fraction_tolerance; }
244 
245     //! Returns the comparison method
strength() const246     fpc::strength       strength() const            { return m_strength; }
247 
248     //! Returns the failing fraction
tested_rel_diff() const249     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      */
operator ()(FPT left,FPT right) const262     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 
276 private:
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  */
293 template<typename FPT>
294 class small_with_tolerance {
295 public:
296     // Public typedefs
297     typedef bool result_type;
298 
299     // Constructor
small_with_tolerance(FPT tolerance)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
operator ()(FPT fpv) const307     bool        operator()( FPT fpv ) const
308     {
309         return fpc::fpc_detail::fpt_abs( fpv ) <= m_tolerance;
310     }
311 
312 private:
313     // Data members
314     FPT         m_tolerance;
315 };
316 
317 // ************************************************************************** //
318 // **************                  is_small                    ************** //
319 // ************************************************************************** //
320 
321 template<typename FPT>
322 inline bool
is_small(FPT fpv,FPT tolerance)323 is_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