1 /*
2  *  Created by Martin on 19/07/2017.
3  *  Copyright 2017 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 
9 #include "catch_approx.h"
10 #include "catch_enforce.h"
11 
12 #include <cmath>
13 #include <limits>
14 
15 namespace {
16 
17 // Performs equivalent check of std::fabs(lhs - rhs) <= margin
18 // But without the subtraction to allow for INFINITY in comparison
marginComparison(double lhs,double rhs,double margin)19 bool marginComparison(double lhs, double rhs, double margin) {
20     return (lhs + margin >= rhs) && (rhs + margin >= lhs);
21 }
22 
23 }
24 
25 namespace Catch {
26 namespace Detail {
27 
Approx(double value)28     Approx::Approx ( double value )
29     :   m_epsilon( std::numeric_limits<float>::epsilon()*100 ),
30         m_margin( 0.0 ),
31         m_scale( 0.0 ),
32         m_value( value )
33     {}
34 
custom()35     Approx Approx::custom() {
36         return Approx( 0 );
37     }
38 
operator -() const39     Approx Approx::operator-() const {
40         auto temp(*this);
41         temp.m_value = -temp.m_value;
42         return temp;
43     }
44 
45 
toString() const46     std::string Approx::toString() const {
47         ReusableStringStream rss;
48         rss << "Approx( " << ::Catch::Detail::stringify( m_value ) << " )";
49         return rss.str();
50     }
51 
equalityComparisonImpl(const double other) const52     bool Approx::equalityComparisonImpl(const double other) const {
53         // First try with fixed margin, then compute margin based on epsilon, scale and Approx's value
54         // Thanks to Richard Harris for his help refining the scaled margin value
55         return marginComparison(m_value, other, m_margin)
56             || marginComparison(m_value, other, m_epsilon * (m_scale + std::fabs(std::isinf(m_value)? 0 : m_value)));
57     }
58 
setMargin(double newMargin)59     void Approx::setMargin(double newMargin) {
60         CATCH_ENFORCE(newMargin >= 0,
61             "Invalid Approx::margin: " << newMargin << '.'
62             << " Approx::Margin has to be non-negative.");
63         m_margin = newMargin;
64     }
65 
setEpsilon(double newEpsilon)66     void Approx::setEpsilon(double newEpsilon) {
67         CATCH_ENFORCE(newEpsilon >= 0 && newEpsilon <= 1.0,
68             "Invalid Approx::epsilon: " << newEpsilon << '.'
69             << " Approx::epsilon has to be in [0, 1]");
70         m_epsilon = newEpsilon;
71     }
72 
73 } // end namespace Detail
74 
75 namespace literals {
operator ""_a(long double val)76     Detail::Approx operator "" _a(long double val) {
77         return Detail::Approx(val);
78     }
operator ""_a(unsigned long long val)79     Detail::Approx operator "" _a(unsigned long long val) {
80         return Detail::Approx(val);
81     }
82 } // end namespace literals
83 
convert(Catch::Detail::Approx const & value)84 std::string StringMaker<Catch::Detail::Approx>::convert(Catch::Detail::Approx const& value) {
85     return value.toString();
86 }
87 
88 } // end namespace Catch
89