diff --git a/include/internal/catch_matchers_floating.cpp b/include/internal/catch_matchers_floating.cpp index c9a79ad1..be969397 100644 --- a/include/internal/catch_matchers_floating.cpp +++ b/include/internal/catch_matchers_floating.cpp @@ -11,6 +11,7 @@ #include "catch_to_string.hpp" #include "catch_tostring.h" +#include #include #include #include @@ -57,7 +58,8 @@ namespace { auto ulpDiff = std::abs(lc - rc); return static_cast(ulpDiff) <= maxUlpDiff; } -} + +} //end anonymous namespace #if defined(CATCH_CONFIG_GLOBAL_NEXTAFTER) @@ -98,6 +100,17 @@ FP step(FP start, FP direction, uint64_t steps) { } return start; } + +namespace { + + // Performs equivalent check of std::fabs(lhs - rhs) <= margin + // But without the subtraction to allow for INFINITY in comparison + bool marginComparison(double lhs, double rhs, double margin) { + return (lhs + margin >= rhs) && (rhs + margin >= lhs); + } + +} + } // end anonymous namespace namespace Matchers { @@ -180,6 +193,25 @@ namespace Floating { //return "is within " + Catch::to_string(m_ulps) + " ULPs of " + ::Catch::Detail::stringify(m_target) + ((m_type == FloatingPointKind::Float)? "f" : ""); } + WithinRelMatcher::WithinRelMatcher(double target, double epsilon): + m_target(target), + m_epsilon(epsilon){ + CATCH_ENFORCE(m_epsilon >= 0., "Relative comparison with epsilon < 0 does not make sense."); + CATCH_ENFORCE(m_epsilon < 1., "Relative comparison with epsilon >= 1 does not make sense."); + } + + bool WithinRelMatcher::match(double const& matchee) const { + const auto relMargin = m_epsilon * std::max(std::fabs(matchee), std::fabs(m_target)); + return marginComparison(matchee, m_target, + std::isinf(relMargin)? 0 : relMargin); + } + + std::string WithinRelMatcher::describe() const { + Catch::ReusableStringStream sstr; + sstr << "and " << m_target << " are within " << m_epsilon * 100. << "% of each other"; + return sstr.str(); + } + }// namespace Floating @@ -196,6 +228,23 @@ Floating::WithinAbsMatcher WithinAbs(double target, double margin) { return Floating::WithinAbsMatcher(target, margin); } +Floating::WithinRelMatcher WithinRel(double target, double eps) { + return Floating::WithinRelMatcher(target, eps); +} + +Floating::WithinRelMatcher WithinRel(double target) { + return Floating::WithinRelMatcher(target, std::numeric_limits::epsilon() * 100); +} + +Floating::WithinRelMatcher WithinRel(float target, float eps) { + return Floating::WithinRelMatcher(target, eps); +} + +Floating::WithinRelMatcher WithinRel(float target) { + return Floating::WithinRelMatcher(target, std::numeric_limits::epsilon() * 100); +} + + } // namespace Matchers } // namespace Catch diff --git a/include/internal/catch_matchers_floating.h b/include/internal/catch_matchers_floating.h index 7db9cc30..0f2ff49f 100644 --- a/include/internal/catch_matchers_floating.h +++ b/include/internal/catch_matchers_floating.h @@ -35,6 +35,20 @@ namespace Matchers { FloatingPointKind m_type; }; + // Given IEEE-754 format for floats and doubles, we can assume + // that float -> double promotion is lossless. Given this, we can + // assume that if we do the standard relative comparison of + // |lhs - rhs| <= epsilon * max(fabs(lhs), fabs(rhs)), then we get + // the same result if we do this for floats, as if we do this for + // doubles that were promoted from floats. + struct WithinRelMatcher : MatcherBase { + WithinRelMatcher(double target, double epsilon); + bool match(double const& matchee) const override; + std::string describe() const override; + private: + double m_target; + double m_epsilon; + }; } // namespace Floating @@ -43,6 +57,12 @@ namespace Matchers { Floating::WithinUlpsMatcher WithinULP(double target, uint64_t maxUlpDiff); Floating::WithinUlpsMatcher WithinULP(float target, uint64_t maxUlpDiff); Floating::WithinAbsMatcher WithinAbs(double target, double margin); + Floating::WithinRelMatcher WithinRel(double target, double eps); + // defaults epsilon to 100*numeric_limits::epsilon() + Floating::WithinRelMatcher WithinRel(double target); + Floating::WithinRelMatcher WithinRel(float target, float eps); + // defaults epsilon to 100*numeric_limits::epsilon() + Floating::WithinRelMatcher WithinRel(float target); } // namespace Matchers } // namespace Catch diff --git a/projects/SelfTest/Baselines/compact.sw.approved.txt b/projects/SelfTest/Baselines/compact.sw.approved.txt index 9a5ce16f..ed47d349 100644 --- a/projects/SelfTest/Baselines/compact.sw.approved.txt +++ b/projects/SelfTest/Baselines/compact.sw.approved.txt @@ -407,6 +407,11 @@ Misc.tests.cpp:: passed: Factorial(1) == 1 for: 1 == 1 Misc.tests.cpp:: passed: Factorial(2) == 2 for: 2 == 2 Misc.tests.cpp:: passed: Factorial(3) == 6 for: 6 == 6 Misc.tests.cpp:: passed: Factorial(10) == 3628800 for: 3628800 (0x) == 3628800 (0x) +Matchers.tests.cpp:: passed: 10., WithinRel(11.1, 0.1) for: 10.0 and 11.1 are within 10% of each other +Matchers.tests.cpp:: passed: 10., !WithinRel(11.2, 0.1) for: 10.0 not and 11.2 are within 10% of each other +Matchers.tests.cpp:: passed: 1., !WithinRel(0., 0.99) for: 1.0 not and 0 are within 99% of each other +Matchers.tests.cpp:: passed: -0., WithinRel(0.) for: -0.0 and 0 are within 2.22045e-12% of each other +Matchers.tests.cpp:: passed: v1, WithinRel(v2) for: 0.0 and 2.22507e-308 are within 2.22045e-12% of each other Matchers.tests.cpp:: passed: 1., WithinAbs(1., 0) for: 1.0 is within 0.0 of 1.0 Matchers.tests.cpp:: passed: 0., WithinAbs(1., 1) for: 0.0 is within 1.0 of 1.0 Matchers.tests.cpp:: passed: 0., !WithinAbs(1., 0.99) for: 0.0 not is within 0.99 of 1.0 @@ -423,9 +428,18 @@ Matchers.tests.cpp:: passed: 1., WithinULP(1., 0) for: 1.0 is withi Matchers.tests.cpp:: passed: -0., WithinULP(0., 0) for: -0.0 is within 0 ULPs of 0.0 ([0.00000000000000000, 0.00000000000000000]) Matchers.tests.cpp:: passed: 1., WithinAbs(1., 0.5) || WithinULP(2., 1) for: 1.0 ( is within 0.5 of 1.0 or is within 1 ULPs of 2.0 ([1.99999999999999978, 2.00000000000000044]) ) Matchers.tests.cpp:: passed: 1., WithinAbs(2., 0.5) || WithinULP(1., 0) for: 1.0 ( is within 0.5 of 2.0 or is within 0 ULPs of 1.0 ([1.00000000000000000, 1.00000000000000000]) ) +Matchers.tests.cpp:: passed: 0.0001, WithinAbs(0., 0.001) || WithinRel(0., 0.1) for: 0.0001 ( is within 0.001 of 0.0 or and 0 are within 10% of each other ) Matchers.tests.cpp:: passed: WithinAbs(1., 0.) Matchers.tests.cpp:: passed: WithinAbs(1., -1.), std::domain_error Matchers.tests.cpp:: passed: WithinULP(1., 0) +Matchers.tests.cpp:: passed: WithinRel(1., 0.) +Matchers.tests.cpp:: passed: WithinRel(1., -0.2), std::domain_error +Matchers.tests.cpp:: passed: WithinRel(1., 1.), std::domain_error +Matchers.tests.cpp:: passed: 10.f, WithinRel(11.1f, 0.1f) for: 10.0f and 11.1 are within 10% of each other +Matchers.tests.cpp:: passed: 10.f, !WithinRel(11.2f, 0.1f) for: 10.0f not and 11.2 are within 10% of each other +Matchers.tests.cpp:: passed: 1.f, !WithinRel(0.f, 0.99f) for: 1.0f not and 0 are within 99% of each other +Matchers.tests.cpp:: passed: -0.f, WithinRel(0.f) for: -0.0f and 0 are within 0.00119209% of each other +Matchers.tests.cpp:: passed: v1, WithinRel(v2) for: 0.0f and 1.17549e-38 are within 0.00119209% of each other Matchers.tests.cpp:: passed: 1.f, WithinAbs(1.f, 0) for: 1.0f is within 0.0 of 1.0 Matchers.tests.cpp:: passed: 0.f, WithinAbs(1.f, 1) for: 0.0f is within 1.0 of 1.0 Matchers.tests.cpp:: passed: 0.f, !WithinAbs(1.f, 0.99f) for: 0.0f not is within 0.9900000095 of 1.0 @@ -443,10 +457,14 @@ Matchers.tests.cpp:: passed: 1.f, WithinULP(1.f, 0) for: 1.0f is wi Matchers.tests.cpp:: passed: -0.f, WithinULP(0.f, 0) for: -0.0f is within 0 ULPs of 0.0f ([0.00000000000000000, 0.00000000000000000]) Matchers.tests.cpp:: passed: 1.f, WithinAbs(1.f, 0.5) || WithinULP(1.f, 1) for: 1.0f ( is within 0.5 of 1.0 or is within 1 ULPs of 1.0f ([0.99999994039535522, 1.00000011920928955]) ) Matchers.tests.cpp:: passed: 1.f, WithinAbs(2.f, 0.5) || WithinULP(1.f, 0) for: 1.0f ( is within 0.5 of 2.0 or is within 0 ULPs of 1.0f ([1.00000000000000000, 1.00000000000000000]) ) +Matchers.tests.cpp:: passed: 0.0001f, WithinAbs(0.f, 0.001f) || WithinRel(0.f, 0.1f) for: 0.0001f ( is within 0.001 of 0.0 or and 0 are within 10% of each other ) Matchers.tests.cpp:: passed: WithinAbs(1.f, 0.f) Matchers.tests.cpp:: passed: WithinAbs(1.f, -1.f), std::domain_error Matchers.tests.cpp:: passed: WithinULP(1.f, 0) Matchers.tests.cpp:: passed: WithinULP(1.f, static_cast(-1)), std::domain_error +Matchers.tests.cpp:: passed: WithinRel(1.f, 0.f) +Matchers.tests.cpp:: passed: WithinRel(1.f, -0.2f), std::domain_error +Matchers.tests.cpp:: passed: WithinRel(1.f, 1.f), std::domain_error Generators.tests.cpp:: passed: i % 2 == 0 for: 0 == 0 Generators.tests.cpp:: passed: i % 2 == 0 for: 0 == 0 Generators.tests.cpp:: passed: i % 2 == 0 for: 0 == 0 diff --git a/projects/SelfTest/Baselines/console.std.approved.txt b/projects/SelfTest/Baselines/console.std.approved.txt index 5a3fb409..0143bbd2 100644 --- a/projects/SelfTest/Baselines/console.std.approved.txt +++ b/projects/SelfTest/Baselines/console.std.approved.txt @@ -1381,5 +1381,5 @@ due to unexpected exception with message: =============================================================================== test cases: 303 | 229 passed | 70 failed | 4 failed as expected -assertions: 1597 | 1445 passed | 131 failed | 21 failed as expected +assertions: 1615 | 1463 passed | 131 failed | 21 failed as expected diff --git a/projects/SelfTest/Baselines/console.sw.approved.txt b/projects/SelfTest/Baselines/console.sw.approved.txt index 1e290592..5c140a96 100644 --- a/projects/SelfTest/Baselines/console.sw.approved.txt +++ b/projects/SelfTest/Baselines/console.sw.approved.txt @@ -3088,6 +3088,46 @@ Misc.tests.cpp:: PASSED: with expansion: 3628800 (0x) == 3628800 (0x) +------------------------------------------------------------------------------- +Floating point matchers: double + Relative +------------------------------------------------------------------------------- +Matchers.tests.cpp: +............................................................................... + +Matchers.tests.cpp:: PASSED: + REQUIRE_THAT( 10., WithinRel(11.1, 0.1) ) +with expansion: + 10.0 and 11.1 are within 10% of each other + +Matchers.tests.cpp:: PASSED: + REQUIRE_THAT( 10., !WithinRel(11.2, 0.1) ) +with expansion: + 10.0 not and 11.2 are within 10% of each other + +Matchers.tests.cpp:: PASSED: + REQUIRE_THAT( 1., !WithinRel(0., 0.99) ) +with expansion: + 1.0 not and 0 are within 99% of each other + +Matchers.tests.cpp:: PASSED: + REQUIRE_THAT( -0., WithinRel(0.) ) +with expansion: + -0.0 and 0 are within 2.22045e-12% of each other + +------------------------------------------------------------------------------- +Floating point matchers: double + Relative + Some subnormal values +------------------------------------------------------------------------------- +Matchers.tests.cpp: +............................................................................... + +Matchers.tests.cpp:: PASSED: + REQUIRE_THAT( v1, WithinRel(v2) ) +with expansion: + 0.0 and 2.22507e-308 are within 2.22045e-12% of each other + ------------------------------------------------------------------------------- Floating point matchers: double Margin @@ -3191,6 +3231,11 @@ with expansion: 1.0 ( is within 0.5 of 2.0 or is within 0 ULPs of 1.0 ([1.00000000000000000, 1.00000000000000000]) ) +Matchers.tests.cpp:: PASSED: + REQUIRE_THAT( 0.0001, WithinAbs(0., 0.001) || WithinRel(0., 0.1) ) +with expansion: + 0.0001 ( is within 0.001 of 0.0 or and 0 are within 10% of each other ) + ------------------------------------------------------------------------------- Floating point matchers: double Constructor validation @@ -3207,6 +3252,55 @@ Matchers.tests.cpp:: PASSED: Matchers.tests.cpp:: PASSED: REQUIRE_NOTHROW( WithinULP(1., 0) ) +Matchers.tests.cpp:: PASSED: + REQUIRE_NOTHROW( WithinRel(1., 0.) ) + +Matchers.tests.cpp:: PASSED: + REQUIRE_THROWS_AS( WithinRel(1., -0.2), std::domain_error ) + +Matchers.tests.cpp:: PASSED: + REQUIRE_THROWS_AS( WithinRel(1., 1.), std::domain_error ) + +------------------------------------------------------------------------------- +Floating point matchers: float + Relative +------------------------------------------------------------------------------- +Matchers.tests.cpp: +............................................................................... + +Matchers.tests.cpp:: PASSED: + REQUIRE_THAT( 10.f, WithinRel(11.1f, 0.1f) ) +with expansion: + 10.0f and 11.1 are within 10% of each other + +Matchers.tests.cpp:: PASSED: + REQUIRE_THAT( 10.f, !WithinRel(11.2f, 0.1f) ) +with expansion: + 10.0f not and 11.2 are within 10% of each other + +Matchers.tests.cpp:: PASSED: + REQUIRE_THAT( 1.f, !WithinRel(0.f, 0.99f) ) +with expansion: + 1.0f not and 0 are within 99% of each other + +Matchers.tests.cpp:: PASSED: + REQUIRE_THAT( -0.f, WithinRel(0.f) ) +with expansion: + -0.0f and 0 are within 0.00119209% of each other + +------------------------------------------------------------------------------- +Floating point matchers: float + Relative + Some subnormal values +------------------------------------------------------------------------------- +Matchers.tests.cpp: +............................................................................... + +Matchers.tests.cpp:: PASSED: + REQUIRE_THAT( v1, WithinRel(v2) ) +with expansion: + 0.0f and 1.17549e-38 are within 0.00119209% of each other + ------------------------------------------------------------------------------- Floating point matchers: float Margin @@ -3316,6 +3410,11 @@ with expansion: 1.0f ( is within 0.5 of 2.0 or is within 0 ULPs of 1.0f ([1. 00000000000000000, 1.00000000000000000]) ) +Matchers.tests.cpp:: PASSED: + REQUIRE_THAT( 0.0001f, WithinAbs(0.f, 0.001f) || WithinRel(0.f, 0.1f) ) +with expansion: + 0.0001f ( is within 0.001 of 0.0 or and 0 are within 10% of each other ) + ------------------------------------------------------------------------------- Floating point matchers: float Constructor validation @@ -3335,6 +3434,15 @@ Matchers.tests.cpp:: PASSED: Matchers.tests.cpp:: PASSED: REQUIRE_THROWS_AS( WithinULP(1.f, static_cast(-1)), std::domain_error ) +Matchers.tests.cpp:: PASSED: + REQUIRE_NOTHROW( WithinRel(1.f, 0.f) ) + +Matchers.tests.cpp:: PASSED: + REQUIRE_THROWS_AS( WithinRel(1.f, -0.2f), std::domain_error ) + +Matchers.tests.cpp:: PASSED: + REQUIRE_THROWS_AS( WithinRel(1.f, 1.f), std::domain_error ) + ------------------------------------------------------------------------------- Generators -- adapters Filtering by predicate @@ -12806,5 +12914,5 @@ Misc.tests.cpp:: PASSED: =============================================================================== test cases: 303 | 213 passed | 86 failed | 4 failed as expected -assertions: 1614 | 1445 passed | 148 failed | 21 failed as expected +assertions: 1632 | 1463 passed | 148 failed | 21 failed as expected diff --git a/projects/SelfTest/Baselines/junit.sw.approved.txt b/projects/SelfTest/Baselines/junit.sw.approved.txt index ed1ef6b7..6003cd73 100644 --- a/projects/SelfTest/Baselines/junit.sw.approved.txt +++ b/projects/SelfTest/Baselines/junit.sw.approved.txt @@ -1,7 +1,7 @@ - + @@ -402,10 +402,14 @@ Message.tests.cpp: + + + + diff --git a/projects/SelfTest/Baselines/xml.sw.approved.txt b/projects/SelfTest/Baselines/xml.sw.approved.txt index f61217b6..27a3a2ad 100644 --- a/projects/SelfTest/Baselines/xml.sw.approved.txt +++ b/projects/SelfTest/Baselines/xml.sw.approved.txt @@ -3657,6 +3657,52 @@ Nor would this +
+ + + 10., WithinRel(11.1, 0.1) + + + 10.0 and 11.1 are within 10% of each other + + + + + 10., !WithinRel(11.2, 0.1) + + + 10.0 not and 11.2 are within 10% of each other + + + + + 1., !WithinRel(0., 0.99) + + + 1.0 not and 0 are within 99% of each other + + + + + -0., WithinRel(0.) + + + -0.0 and 0 are within 2.22045e-12% of each other + + +
+ + + v1, WithinRel(v2) + + + 0.0 and 2.22507e-308 are within 2.22045e-12% of each other + + + +
+ +
@@ -3792,7 +3838,15 @@ Nor would this 1.0 ( is within 0.5 of 2.0 or is within 0 ULPs of 1.0 ([1.00000000000000000, 1.00000000000000000]) ) - + + + 0.0001, WithinAbs(0., 0.001) || WithinRel(0., 0.1) + + + 0.0001 ( is within 0.001 of 0.0 or and 0 are within 10% of each other ) + + +
@@ -3819,11 +3873,81 @@ Nor would this WithinULP(1., 0) - + + + WithinRel(1., 0.) + + + WithinRel(1., 0.) + + + + + WithinRel(1., -0.2), std::domain_error + + + WithinRel(1., -0.2), std::domain_error + + + + + WithinRel(1., 1.), std::domain_error + + + WithinRel(1., 1.), std::domain_error + + +
+
+ + + 10.f, WithinRel(11.1f, 0.1f) + + + 10.0f and 11.1 are within 10% of each other + + + + + 10.f, !WithinRel(11.2f, 0.1f) + + + 10.0f not and 11.2 are within 10% of each other + + + + + 1.f, !WithinRel(0.f, 0.99f) + + + 1.0f not and 0 are within 99% of each other + + + + + -0.f, WithinRel(0.f) + + + -0.0f and 0 are within 0.00119209% of each other + + +
+ + + v1, WithinRel(v2) + + + 0.0f and 1.17549e-38 are within 0.00119209% of each other + + + +
+ +
@@ -3967,7 +4091,15 @@ Nor would this 1.0f ( is within 0.5 of 2.0 or is within 0 ULPs of 1.0f ([1.00000000000000000, 1.00000000000000000]) ) - + + + 0.0001f, WithinAbs(0.f, 0.001f) || WithinRel(0.f, 0.1f) + + + 0.0001f ( is within 0.001 of 0.0 or and 0 are within 10% of each other ) + + +
@@ -4002,7 +4134,31 @@ Nor would this WithinULP(1.f, static_cast<uint64_t>(-1)), std::domain_error - + + + WithinRel(1.f, 0.f) + + + WithinRel(1.f, 0.f) + + + + + WithinRel(1.f, -0.2f), std::domain_error + + + WithinRel(1.f, -0.2f), std::domain_error + + + + + WithinRel(1.f, 1.f), std::domain_error + + + WithinRel(1.f, 1.f), std::domain_error + + +
@@ -15220,7 +15376,7 @@ loose text artifact
- + - + diff --git a/projects/SelfTest/UsageTests/Matchers.tests.cpp b/projects/SelfTest/UsageTests/Matchers.tests.cpp index 8e9148b3..ae05a73c 100644 --- a/projects/SelfTest/UsageTests/Matchers.tests.cpp +++ b/projects/SelfTest/UsageTests/Matchers.tests.cpp @@ -339,6 +339,20 @@ namespace { namespace MatchersTests { } TEST_CASE("Floating point matchers: float", "[matchers][floating-point]") { + SECTION("Relative") { + REQUIRE_THAT(10.f, WithinRel(11.1f, 0.1f)); + REQUIRE_THAT(10.f, !WithinRel(11.2f, 0.1f)); + REQUIRE_THAT( 1.f, !WithinRel(0.f, 0.99f)); + REQUIRE_THAT(-0.f, WithinRel(0.f)); + SECTION("Some subnormal values") { + auto v1 = std::numeric_limits::min(); + auto v2 = v1; + for (int i = 0; i < 5; ++i) { + v2 = std::nextafter(v1, 0); + } + REQUIRE_THAT(v1, WithinRel(v2)); + } + } SECTION("Margin") { REQUIRE_THAT(1.f, WithinAbs(1.f, 0)); REQUIRE_THAT(0.f, WithinAbs(1.f, 1)); @@ -366,6 +380,7 @@ namespace { namespace MatchersTests { SECTION("Composed") { REQUIRE_THAT(1.f, WithinAbs(1.f, 0.5) || WithinULP(1.f, 1)); REQUIRE_THAT(1.f, WithinAbs(2.f, 0.5) || WithinULP(1.f, 0)); + REQUIRE_THAT(0.0001f, WithinAbs(0.f, 0.001f) || WithinRel(0.f, 0.1f)); } SECTION("Constructor validation") { REQUIRE_NOTHROW(WithinAbs(1.f, 0.f)); @@ -373,10 +388,28 @@ namespace { namespace MatchersTests { REQUIRE_NOTHROW(WithinULP(1.f, 0)); REQUIRE_THROWS_AS(WithinULP(1.f, static_cast(-1)), std::domain_error); + + REQUIRE_NOTHROW(WithinRel(1.f, 0.f)); + REQUIRE_THROWS_AS(WithinRel(1.f, -0.2f), std::domain_error); + REQUIRE_THROWS_AS(WithinRel(1.f, 1.f), std::domain_error); } } TEST_CASE("Floating point matchers: double", "[matchers][floating-point]") { + SECTION("Relative") { + REQUIRE_THAT(10., WithinRel(11.1, 0.1)); + REQUIRE_THAT(10., !WithinRel(11.2, 0.1)); + REQUIRE_THAT(1., !WithinRel(0., 0.99)); + REQUIRE_THAT(-0., WithinRel(0.)); + SECTION("Some subnormal values") { + auto v1 = std::numeric_limits::min(); + auto v2 = v1; + for (int i = 0; i < 5; ++i) { + v2 = std::nextafter(v1, 0); + } + REQUIRE_THAT(v1, WithinRel(v2)); + } + } SECTION("Margin") { REQUIRE_THAT(1., WithinAbs(1., 0)); REQUIRE_THAT(0., WithinAbs(1., 1)); @@ -402,12 +435,17 @@ namespace { namespace MatchersTests { SECTION("Composed") { REQUIRE_THAT(1., WithinAbs(1., 0.5) || WithinULP(2., 1)); REQUIRE_THAT(1., WithinAbs(2., 0.5) || WithinULP(1., 0)); + REQUIRE_THAT(0.0001, WithinAbs(0., 0.001) || WithinRel(0., 0.1)); } SECTION("Constructor validation") { REQUIRE_NOTHROW(WithinAbs(1., 0.)); REQUIRE_THROWS_AS(WithinAbs(1., -1.), std::domain_error); REQUIRE_NOTHROW(WithinULP(1., 0)); + + REQUIRE_NOTHROW(WithinRel(1., 0.)); + REQUIRE_THROWS_AS(WithinRel(1., -0.2), std::domain_error); + REQUIRE_THROWS_AS(WithinRel(1., 1.), std::domain_error); } } @@ -415,6 +453,13 @@ namespace { namespace MatchersTests { REQUIRE_THAT(NAN, !WithinAbs(NAN, 0)); REQUIRE_THAT(NAN, !(WithinAbs(NAN, 100) || WithinULP(NAN, 123))); REQUIRE_THAT(NAN, !WithinULP(NAN, 123)); + REQUIRE_THAT(INFINITY, WithinRel(INFINITY)); + REQUIRE_THAT(-INFINITY, !WithinRel(INFINITY)); + REQUIRE_THAT(1., !WithinRel(INFINITY)); + REQUIRE_THAT(INFINITY, !WithinRel(1.)); + REQUIRE_THAT(NAN, !WithinRel(NAN)); + REQUIRE_THAT(1., !WithinRel(NAN)); + REQUIRE_THAT(NAN, !WithinRel(1.)); } TEST_CASE("Arbitrary predicate matcher", "[matchers][generic]") {