• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *  Created by Martin on 07/11/2017.
3  *
4  * Distributed under the Boost Software License, Version 1.0. (See accompanying
5  * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6  */
7 
8 #include "catch_matchers_floating.h"
9 #include "catch_enforce.h"
10 #include "catch_polyfills.hpp"
11 #include "catch_to_string.hpp"
12 #include "catch_tostring.h"
13 
14 #include <cstdlib>
15 #include <cstdint>
16 #include <cstring>
17 
18 namespace Catch {
19 namespace Matchers {
20 namespace Floating {
21 enum class FloatingPointKind : uint8_t {
22     Float,
23     Double
24 };
25 }
26 }
27 }
28 
29 namespace {
30 
31 template <typename T>
32 struct Converter;
33 
34 template <>
35 struct Converter<float> {
36     static_assert(sizeof(float) == sizeof(int32_t), "Important ULP matcher assumption violated");
Converter__anon82af003a0111::Converter37     Converter(float f) {
38         std::memcpy(&i, &f, sizeof(f));
39     }
40     int32_t i;
41 };
42 
43 template <>
44 struct Converter<double> {
45     static_assert(sizeof(double) == sizeof(int64_t), "Important ULP matcher assumption violated");
Converter__anon82af003a0111::Converter46     Converter(double d) {
47         std::memcpy(&i, &d, sizeof(d));
48     }
49     int64_t i;
50 };
51 
52 template <typename T>
convert(T t)53 auto convert(T t) -> Converter<T> {
54     return Converter<T>(t);
55 }
56 
57 template <typename FP>
almostEqualUlps(FP lhs,FP rhs,int maxUlpDiff)58 bool almostEqualUlps(FP lhs, FP rhs, int maxUlpDiff) {
59     // Comparison with NaN should always be false.
60     // This way we can rule it out before getting into the ugly details
61     if (Catch::isnan(lhs) || Catch::isnan(rhs)) {
62         return false;
63     }
64 
65     auto lc = convert(lhs);
66     auto rc = convert(rhs);
67 
68     if ((lc.i < 0) != (rc.i < 0)) {
69         // Potentially we can have +0 and -0
70         return lhs == rhs;
71     }
72 
73     auto ulpDiff = std::abs(lc.i - rc.i);
74     return ulpDiff <= maxUlpDiff;
75 }
76 
77 }
78 
79 
80 namespace Catch {
81 namespace Matchers {
82 namespace Floating {
WithinAbsMatcher(double target,double margin)83     WithinAbsMatcher::WithinAbsMatcher(double target, double margin)
84         :m_target{ target }, m_margin{ margin } {
85         CATCH_ENFORCE(margin >= 0, "Invalid margin: " << margin << '.'
86             << " Margin has to be non-negative.");
87     }
88 
89     // Performs equivalent check of std::fabs(lhs - rhs) <= margin
90     // But without the subtraction to allow for INFINITY in comparison
match(double const & matchee) const91     bool WithinAbsMatcher::match(double const& matchee) const {
92         return (matchee + m_margin >= m_target) && (m_target + m_margin >= matchee);
93     }
94 
describe() const95     std::string WithinAbsMatcher::describe() const {
96         return "is within " + ::Catch::Detail::stringify(m_margin) + " of " + ::Catch::Detail::stringify(m_target);
97     }
98 
99 
WithinUlpsMatcher(double target,int ulps,FloatingPointKind baseType)100     WithinUlpsMatcher::WithinUlpsMatcher(double target, int ulps, FloatingPointKind baseType)
101         :m_target{ target }, m_ulps{ ulps }, m_type{ baseType } {
102         CATCH_ENFORCE(ulps >= 0, "Invalid ULP setting: " << ulps << '.'
103             << " ULPs have to be non-negative.");
104     }
105 
106 #if defined(__clang__)
107 #pragma clang diagnostic push
108 // Clang <3.5 reports on the default branch in the switch below
109 #pragma clang diagnostic ignored "-Wunreachable-code"
110 #endif
111 
match(double const & matchee) const112     bool WithinUlpsMatcher::match(double const& matchee) const {
113         switch (m_type) {
114         case FloatingPointKind::Float:
115             return almostEqualUlps<float>(static_cast<float>(matchee), static_cast<float>(m_target), m_ulps);
116         case FloatingPointKind::Double:
117             return almostEqualUlps<double>(matchee, m_target, m_ulps);
118         default:
119             CATCH_INTERNAL_ERROR( "Unknown FloatingPointKind value" );
120         }
121     }
122 
123 #if defined(__clang__)
124 #pragma clang diagnostic pop
125 #endif
126 
describe() const127     std::string WithinUlpsMatcher::describe() const {
128         return "is within " + Catch::to_string(m_ulps) + " ULPs of " + ::Catch::Detail::stringify(m_target) + ((m_type == FloatingPointKind::Float)? "f" : "");
129     }
130 
131 }// namespace Floating
132 
133 
134 
WithinULP(double target,int maxUlpDiff)135 Floating::WithinUlpsMatcher WithinULP(double target, int maxUlpDiff) {
136     return Floating::WithinUlpsMatcher(target, maxUlpDiff, Floating::FloatingPointKind::Double);
137 }
138 
WithinULP(float target,int maxUlpDiff)139 Floating::WithinUlpsMatcher WithinULP(float target, int maxUlpDiff) {
140     return Floating::WithinUlpsMatcher(target, maxUlpDiff, Floating::FloatingPointKind::Float);
141 }
142 
WithinAbs(double target,double margin)143 Floating::WithinAbsMatcher WithinAbs(double target, double margin) {
144     return Floating::WithinAbsMatcher(target, margin);
145 }
146 
147 } // namespace Matchers
148 } // namespace Catch
149 
150