1 /*
2 * Created by Phil on 28/04/2011.
3 * Copyright 2010 Two Blue Cubes Ltd. All rights reserved.
4 *
5 * Distributed under the Boost Software License, Version 1.0. (See accompanying
6 * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7 */
8 #ifndef TWOBLUECUBES_CATCH_APPROX_HPP_INCLUDED
9 #define TWOBLUECUBES_CATCH_APPROX_HPP_INCLUDED
10
11 #include "catch_tostring.h"
12
13 #include <cmath>
14 #include <limits>
15
16 #if defined(CATCH_CONFIG_CPP11_TYPE_TRAITS)
17 #include <type_traits>
18 #endif
19
20 namespace Catch {
21 namespace Detail {
22
23 class Approx {
24 public:
Approx(double value)25 explicit Approx ( double value )
26 : m_epsilon( std::numeric_limits<float>::epsilon()*100 ),
27 m_margin( 0.0 ),
28 m_scale( 1.0 ),
29 m_value( value )
30 {}
31
custom()32 static Approx custom() {
33 return Approx( 0 );
34 }
35
36 #if defined(CATCH_CONFIG_CPP11_TYPE_TRAITS)
37
38 template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
operator ()(T value)39 Approx operator()( T value ) {
40 Approx approx( static_cast<double>(value) );
41 approx.epsilon( m_epsilon );
42 approx.margin( m_margin );
43 approx.scale( m_scale );
44 return approx;
45 }
46
47 template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
Approx(T value)48 explicit Approx( T value ): Approx(static_cast<double>(value))
49 {}
50
51
52 template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
operator ==(const T & lhs,Approx const & rhs)53 friend bool operator == ( const T& lhs, Approx const& rhs ) {
54 // Thanks to Richard Harris for his help refining this formula
55 auto lhs_v = double(lhs);
56 bool relativeOK = std::fabs(lhs_v - rhs.m_value) < rhs.m_epsilon * (rhs.m_scale + (std::max)(std::fabs(lhs_v), std::fabs(rhs.m_value)));
57 if (relativeOK) {
58 return true;
59 }
60
61 return std::fabs(lhs_v - rhs.m_value) <= rhs.m_margin;
62 }
63
64 template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
operator ==(Approx const & lhs,const T & rhs)65 friend bool operator == ( Approx const& lhs, const T& rhs ) {
66 return operator==( rhs, lhs );
67 }
68
69 template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
operator !=(T lhs,Approx const & rhs)70 friend bool operator != ( T lhs, Approx const& rhs ) {
71 return !operator==( lhs, rhs );
72 }
73
74 template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
operator !=(Approx const & lhs,T rhs)75 friend bool operator != ( Approx const& lhs, T rhs ) {
76 return !operator==( rhs, lhs );
77 }
78
79 template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
operator <=(T lhs,Approx const & rhs)80 friend bool operator <= ( T lhs, Approx const& rhs ) {
81 return double(lhs) < rhs.m_value || lhs == rhs;
82 }
83
84 template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
operator <=(Approx const & lhs,T rhs)85 friend bool operator <= ( Approx const& lhs, T rhs ) {
86 return lhs.m_value < double(rhs) || lhs == rhs;
87 }
88
89 template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
operator >=(T lhs,Approx const & rhs)90 friend bool operator >= ( T lhs, Approx const& rhs ) {
91 return double(lhs) > rhs.m_value || lhs == rhs;
92 }
93
94 template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
operator >=(Approx const & lhs,T rhs)95 friend bool operator >= ( Approx const& lhs, T rhs ) {
96 return lhs.m_value > double(rhs) || lhs == rhs;
97 }
98
99 template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
epsilon(T newEpsilon)100 Approx& epsilon( T newEpsilon ) {
101 m_epsilon = double(newEpsilon);
102 return *this;
103 }
104
105 template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
margin(T newMargin)106 Approx& margin( T newMargin ) {
107 m_margin = double(newMargin);
108 return *this;
109 }
110
111 template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
scale(T newScale)112 Approx& scale( T newScale ) {
113 m_scale = double(newScale);
114 return *this;
115 }
116
117 #else
118
operator ()(double value)119 Approx operator()( double value ) {
120 Approx approx( value );
121 approx.epsilon( m_epsilon );
122 approx.margin( m_margin );
123 approx.scale( m_scale );
124 return approx;
125 }
126
127
operator ==(double lhs,Approx const & rhs)128 friend bool operator == ( double lhs, Approx const& rhs ) {
129 // Thanks to Richard Harris for his help refining this formula
130 bool relativeOK = std::fabs( lhs - rhs.m_value ) < rhs.m_epsilon * (rhs.m_scale + (std::max)( std::fabs(lhs), std::fabs(rhs.m_value) ) );
131 if (relativeOK) {
132 return true;
133 }
134 return std::fabs(lhs - rhs.m_value) <= rhs.m_margin;
135 }
136
operator ==(Approx const & lhs,double rhs)137 friend bool operator == ( Approx const& lhs, double rhs ) {
138 return operator==( rhs, lhs );
139 }
140
operator !=(double lhs,Approx const & rhs)141 friend bool operator != ( double lhs, Approx const& rhs ) {
142 return !operator==( lhs, rhs );
143 }
144
operator !=(Approx const & lhs,double rhs)145 friend bool operator != ( Approx const& lhs, double rhs ) {
146 return !operator==( rhs, lhs );
147 }
148
operator <=(double lhs,Approx const & rhs)149 friend bool operator <= ( double lhs, Approx const& rhs ) {
150 return lhs < rhs.m_value || lhs == rhs;
151 }
152
operator <=(Approx const & lhs,double rhs)153 friend bool operator <= ( Approx const& lhs, double rhs ) {
154 return lhs.m_value < rhs || lhs == rhs;
155 }
156
operator >=(double lhs,Approx const & rhs)157 friend bool operator >= ( double lhs, Approx const& rhs ) {
158 return lhs > rhs.m_value || lhs == rhs;
159 }
160
operator >=(Approx const & lhs,double rhs)161 friend bool operator >= ( Approx const& lhs, double rhs ) {
162 return lhs.m_value > rhs || lhs == rhs;
163 }
164
epsilon(double newEpsilon)165 Approx& epsilon( double newEpsilon ) {
166 m_epsilon = newEpsilon;
167 return *this;
168 }
169
margin(double newMargin)170 Approx& margin( double newMargin ) {
171 m_margin = newMargin;
172 return *this;
173 }
174
scale(double newScale)175 Approx& scale( double newScale ) {
176 m_scale = newScale;
177 return *this;
178 }
179 #endif
180
toString() const181 std::string toString() const {
182 std::ostringstream oss;
183 oss << "Approx( " << Catch::toString( m_value ) << " )";
184 return oss.str();
185 }
186
187 private:
188 double m_epsilon;
189 double m_margin;
190 double m_scale;
191 double m_value;
192 };
193 }
194
195 template<>
toString(Detail::Approx const & value)196 inline std::string toString<Detail::Approx>( Detail::Approx const& value ) {
197 return value.toString();
198 }
199
200 } // end namespace Catch
201
202 #endif // TWOBLUECUBES_CATCH_APPROX_HPP_INCLUDED
203