11
11
#include < catch2/catch_tostring.hpp>
12
12
13
13
#include < type_traits>
14
+ #include < limits>
14
15
15
16
namespace Catch {
16
17
17
18
class Approx {
18
19
private:
19
- bool equalityComparisonImpl (double other) const ;
20
- // Sets and validates the new margin (margin >= 0)
21
- void setMargin (double margin);
20
+ // Performs equivalent check of std::fabs(lhs - rhs) <= margin
21
+ // But without the subtraction to allow for INFINITY in comparison
22
+ constexpr bool marginComparison (double lhs, double rhs, double margin) const {
23
+ return (lhs + margin >= rhs) && (rhs + margin >= lhs);
24
+ }
25
+
26
+ constexpr bool equalityComparisonImpl (double other) const {
27
+ // First try with fixed margin, then compute margin based on epsilon, scale and Approx's value
28
+ // Thanks to Richard Harris for his help refining the scaled margin value
29
+ return marginComparison (m_value, other, m_margin)
30
+ || marginComparison (m_value, other, m_epsilon * (m_scale + std::fabs (std::isinf (m_value)? 0 : m_value)));
31
+ }
32
+
22
33
// Sets and validates the new epsilon (0 < epsilon < 1)
23
- void setEpsilon (double epsilon);
34
+ constexpr void setEpsilon (double epsilon) {
35
+ // CATCH_ENFORCE(epsilon >= 0 && epsilon <= 1.0,
36
+ // "Invalid Approx::epsilon: " << epsilon << '.'
37
+ // << " Approx::epsilon has to be in [0, 1]");
38
+ m_epsilon = epsilon;
39
+ }
40
+
41
+ // Sets and validates the new margin (margin >= 0)
42
+ constexpr void setMargin (double margin) {
43
+ // CATCH_ENFORCE(margin >= 0,
44
+ // "Invalid Approx::margin: " << margin << '.'
45
+ // << " Approx::Margin has to be non-negative.");
46
+ m_margin = margin;
47
+ }
24
48
25
49
public:
26
- explicit Approx ( double value );
50
+ explicit constexpr Approx ( double value )
51
+ : m_epsilon( static_cast <double >(std::numeric_limits<float >::epsilon())*100. ),
52
+ m_margin( 0.0 ),
53
+ m_scale( 0.0 ),
54
+ m_value( value )
55
+ {}
27
56
28
- static Approx custom ();
57
+ static constexpr Approx custom () {
58
+ return Approx ( 0 );
59
+ }
29
60
30
- Approx operator -() const ;
61
+ constexpr Approx operator -() const {
62
+ auto temp (*this );
63
+ temp.m_value = -temp.m_value ;
64
+ return temp;
65
+ }
31
66
32
67
template <typename T, typename = std::enable_if_t <std::is_constructible<double , T>::value>>
33
- Approx operator ()( T const & value ) const {
68
+ constexpr Approx operator ()( T const & value ) const {
34
69
Approx approx ( static_cast <double >(value) );
35
70
approx.m_epsilon = m_epsilon;
36
71
approx.m_margin = m_margin;
@@ -39,67 +74,67 @@ namespace Catch {
39
74
}
40
75
41
76
template <typename T, typename = std::enable_if_t <std::is_constructible<double , T>::value>>
42
- explicit Approx ( T const & value ): Approx(static_cast <double >(value))
77
+ explicit constexpr Approx ( T const & value ): Approx(static_cast <double >(value))
43
78
{}
44
79
45
80
46
81
template <typename T, typename = std::enable_if_t <std::is_constructible<double , T>::value>>
47
- friend bool operator == ( const T& lhs, Approx const & rhs ) {
82
+ friend constexpr bool operator == ( const T& lhs, Approx const & rhs ) {
48
83
auto lhs_v = static_cast <double >(lhs);
49
84
return rhs.equalityComparisonImpl (lhs_v);
50
85
}
51
86
52
87
template <typename T, typename = std::enable_if_t <std::is_constructible<double , T>::value>>
53
- friend bool operator == ( Approx const & lhs, const T& rhs ) {
88
+ friend constexpr bool operator == ( Approx const & lhs, const T& rhs ) {
54
89
return operator ==( rhs, lhs );
55
90
}
56
91
57
92
template <typename T, typename = std::enable_if_t <std::is_constructible<double , T>::value>>
58
- friend bool operator != ( T const & lhs, Approx const & rhs ) {
93
+ friend constexpr bool operator != ( T const & lhs, Approx const & rhs ) {
59
94
return !operator ==( lhs, rhs );
60
95
}
61
96
62
97
template <typename T, typename = std::enable_if_t <std::is_constructible<double , T>::value>>
63
- friend bool operator != ( Approx const & lhs, T const & rhs ) {
98
+ friend constexpr bool operator != ( Approx const & lhs, T const & rhs ) {
64
99
return !operator ==( rhs, lhs );
65
100
}
66
101
67
102
template <typename T, typename = std::enable_if_t <std::is_constructible<double , T>::value>>
68
- friend bool operator <= ( T const & lhs, Approx const & rhs ) {
103
+ friend constexpr bool operator <= ( T const & lhs, Approx const & rhs ) {
69
104
return static_cast <double >(lhs) < rhs.m_value || lhs == rhs;
70
105
}
71
106
72
107
template <typename T, typename = std::enable_if_t <std::is_constructible<double , T>::value>>
73
- friend bool operator <= ( Approx const & lhs, T const & rhs ) {
108
+ friend constexpr bool operator <= ( Approx const & lhs, T const & rhs ) {
74
109
return lhs.m_value < static_cast <double >(rhs) || lhs == rhs;
75
110
}
76
111
77
112
template <typename T, typename = std::enable_if_t <std::is_constructible<double , T>::value>>
78
- friend bool operator >= ( T const & lhs, Approx const & rhs ) {
113
+ friend constexpr bool operator >= ( T const & lhs, Approx const & rhs ) {
79
114
return static_cast <double >(lhs) > rhs.m_value || lhs == rhs;
80
115
}
81
116
82
117
template <typename T, typename = std::enable_if_t <std::is_constructible<double , T>::value>>
83
- friend bool operator >= ( Approx const & lhs, T const & rhs ) {
118
+ friend constexpr bool operator >= ( Approx const & lhs, T const & rhs ) {
84
119
return lhs.m_value > static_cast <double >(rhs) || lhs == rhs;
85
120
}
86
121
87
122
template <typename T, typename = std::enable_if_t <std::is_constructible<double , T>::value>>
88
- Approx& epsilon ( T const & newEpsilon ) {
123
+ constexpr Approx& epsilon ( T const & newEpsilon ) {
89
124
const auto epsilonAsDouble = static_cast <double >(newEpsilon);
90
125
setEpsilon (epsilonAsDouble);
91
126
return *this ;
92
127
}
93
128
94
129
template <typename T, typename = std::enable_if_t <std::is_constructible<double , T>::value>>
95
- Approx& margin ( T const & newMargin ) {
130
+ constexpr Approx& margin ( T const & newMargin ) {
96
131
const auto marginAsDouble = static_cast <double >(newMargin);
97
132
setMargin (marginAsDouble);
98
133
return *this ;
99
134
}
100
135
101
136
template <typename T, typename = std::enable_if_t <std::is_constructible<double , T>::value>>
102
- Approx& scale ( T const & newScale ) {
137
+ constexpr Approx& scale ( T const & newScale ) {
103
138
m_scale = static_cast <double >(newScale);
104
139
return *this ;
105
140
}
@@ -114,8 +149,12 @@ namespace Catch {
114
149
};
115
150
116
151
namespace literals {
117
- Approx operator " " _a(long double val);
118
- Approx operator " " _a(unsigned long long val);
152
+ constexpr Approx operator " " _a(long double val) {
153
+ return Approx (val);
154
+ }
155
+ constexpr Approx operator " " _a(unsigned long long val) {
156
+ return Approx (val);
157
+ }
119
158
} // end namespace literals
120
159
121
160
template <>
0 commit comments