diff --git a/CMakeLists.txt b/CMakeLists.txt index e3ea7ca1..a13e100b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -140,6 +140,7 @@ set(INTERNAL_HEADERS ${HEADER_DIR}/internal/catch_leak_detector.h ${HEADER_DIR}/internal/catch_list.h ${HEADER_DIR}/internal/catch_matchers.h + ${HEADER_DIR}/internal/catch_matchers_floating.h ${HEADER_DIR}/internal/catch_matchers_string.h ${HEADER_DIR}/internal/catch_matchers_vector.h ${HEADER_DIR}/internal/catch_message.h @@ -206,6 +207,7 @@ set(IMPL_SOURCES ${HEADER_DIR}/internal/catch_list.cpp ${HEADER_DIR}/internal/catch_leak_detector.cpp ${HEADER_DIR}/internal/catch_matchers.cpp + ${HEADER_DIR}/internal/catch_matchers_floating.cpp ${HEADER_DIR}/internal/catch_matchers_string.cpp ${HEADER_DIR}/internal/catch_message.cpp ${HEADER_DIR}/internal/catch_registry_hub.cpp diff --git a/docs/matchers.md b/docs/matchers.md index 647ec64f..9717cae8 100644 --- a/docs/matchers.md +++ b/docs/matchers.md @@ -36,10 +36,19 @@ REQUIRE_THAT( str, ``` ## Built in matchers -Currently Catch has some string matchers and some vector matchers. They are in the `Catch::Matchers` and `Catch` namespaces. +Catch currently provides some matchers, they are in the `Catch::Matchers` and `Catch` namespaces. + +### String matchers The string matchers are `StartsWith`, `EndsWith`, `Contains` and `Equals`. Each of them also takes an optional second argument, that decides case sensitivity (by-default, they are case sensitive). + +### Vector matchers The vector matchers are `Contains`, `VectorContains` and `Equals`. `VectorContains` looks for a single element in the matched vector, `Contains` looks for a set (vector) of elements inside the matched vector. +### Floating point matchers +The floating point matchers are `WithinULP` and `WithinAbs`. `WithinAbs` accepts floating point numbers that are within a certain margin of target. `WithinULP` performs an [ULP](https://en.wikipedia.org/wiki/Unit_in_the_last_place)-based comparison of two floating point numbers and accepts them if they are less than certain number of ULPs apart. + +Do note that ULP-based checks only make sense when both compared numbers are of the same type and `WithinULP` will use type of its argument as the target type. This means that `WithinULP(1.f, 1)` will expect to compare `float`s, but `WithinULP(1., 1)` will expect to compare `double`s. + ## Custom matchers It's easy to provide your own matchers to extend Catch or just to work with your own types. diff --git a/include/internal/catch_capture_matchers.h b/include/internal/catch_capture_matchers.h index 701ba1f6..77366f33 100644 --- a/include/internal/catch_capture_matchers.h +++ b/include/internal/catch_capture_matchers.h @@ -10,6 +10,7 @@ #include "catch_capture.hpp" #include "catch_matchers.h" +#include "catch_matchers_floating.h" #include "catch_matchers_string.h" #include "catch_matchers_vector.h" diff --git a/include/internal/catch_matchers_floating.cpp b/include/internal/catch_matchers_floating.cpp new file mode 100644 index 00000000..a7c86e44 --- /dev/null +++ b/include/internal/catch_matchers_floating.cpp @@ -0,0 +1,144 @@ +/* + * Created by Martin on 07/11/2017. + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ + +#include "catch_matchers_floating.h" + +#include +#include +#include +#include + +namespace Catch { +namespace Matchers { +namespace Floating { +enum class FloatingPointKind : uint8_t { + Float, + Double +}; +} +} +} + +namespace { +union Float_t { + Float_t(float num = 0.0f) : f(num) {} + // Portable extraction of components. + bool Negative() const { return i < 0; } + int32_t RawMantissa() const { return i & ((1 << 23) - 1); } + int32_t RawExponent() const { return (i >> 23) & 0xFF; } + + int32_t i; + float f; +}; + +template +struct Converter; + +template <> +struct Converter { + static_assert(sizeof(float) == sizeof(int32_t), "Important ULP matcher assumption violated"); + Converter(float f) { + std::memcpy(&i, &f, sizeof(f)); + } + int32_t i; +}; + +template <> +struct Converter { + static_assert(sizeof(double) == sizeof(int64_t), "Important ULP matcher assumption violated"); + Converter(double d) { + std::memcpy(&i, &d, sizeof(d)); + } + int64_t i; +}; + +template +auto convert(T t) -> Converter { + return Converter(t); +} + +template +bool almostEqualUlps(FP lhs, FP rhs, int maxUlpDiff) { + // Comparison with NaN should always be false. + // This way we can rule it out before getting into the ugly details + if (std::isnan(lhs) || std::isnan(rhs)) { + return false; + } + + auto lc = convert(lhs); + auto rc = convert(rhs); + + if ((lc.i < 0) != (rc.i < 0)) { + // Potentially we can have +0 and -0 + return lhs == rhs; + } + + auto ulpDiff = abs(lc.i - rc.i); + return ulpDiff <= maxUlpDiff; +} + +} + + +namespace Catch { +namespace Matchers { +namespace Floating { + WithinAbsMatcher::WithinAbsMatcher(double target, double margin) + :m_target{ target }, m_margin{ margin } {} + + // Performs equivalent check of std::fabs(lhs - rhs) <= margin + // But without the subtraction to allow for INFINITY in comparison + bool WithinAbsMatcher::match(double const& matchee) const { + return (matchee + m_margin >= m_target) && (m_target + m_margin >= m_margin); + } + + std::string WithinAbsMatcher::describe() const { + return "is within " + std::to_string(m_margin) + " of " + std::to_string(m_target); + } + + + WithinUlpsMatcher::WithinUlpsMatcher(double target, int ulps, FloatingPointKind baseType) + :m_target{ target }, m_ulps{ ulps }, m_type{ baseType } { + if (m_ulps < 0) { + throw std::domain_error("Expected ulp difference has to be >0"); + } + } + + bool WithinUlpsMatcher::match(double const& matchee) const { + switch (m_type) { + case FloatingPointKind::Float: + return almostEqualUlps(static_cast(matchee), static_cast(m_target), m_ulps); + case FloatingPointKind::Double: + return almostEqualUlps(matchee, m_target, m_ulps); + default: + throw std::domain_error("Unknown FloatingPointKind value"); + } + } + + std::string WithinUlpsMatcher::describe() const { + return "is within " + std::to_string(m_ulps) + " ULPs of " + std::to_string(m_target) + ((m_type == FloatingPointKind::Float)? "f" : ""); + } + +}// namespace Floating + + + +Floating::WithinUlpsMatcher WithinULP(double target, int maxUlpDiff) { + return Floating::WithinUlpsMatcher(target, maxUlpDiff, Floating::FloatingPointKind::Double); +} + +Floating::WithinUlpsMatcher WithinULP(float target, int maxUlpDiff) { + return Floating::WithinUlpsMatcher(target, maxUlpDiff, Floating::FloatingPointKind::Float); +} + +Floating::WithinAbsMatcher WithinAbs(double target, double margin) { + return Floating::WithinAbsMatcher(target, margin); +} + +} // namespace Matchers +} // namespace Catch + diff --git a/include/internal/catch_matchers_floating.h b/include/internal/catch_matchers_floating.h new file mode 100644 index 00000000..7b3245ce --- /dev/null +++ b/include/internal/catch_matchers_floating.h @@ -0,0 +1,59 @@ +/* + * Created by Martin on 07/11/2017. + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ +#ifndef TWOBLUECUBES_CATCH_MATCHERS_FLOATING_H_INCLUDED +#define TWOBLUECUBES_CATCH_MATCHERS_FLOATING_H_INCLUDED + +#include "catch_matchers.h" + +#include +#include + +namespace Catch { +namespace Matchers { + + namespace Floating { + + enum class FloatingPointKind : uint8_t; + + struct WithinAbsMatcher : MatcherBase { + WithinAbsMatcher(double target, double margin); + bool match(double const& matchee) const override; + std::string describe() const override; + private: + double m_target; + double m_margin; + }; + + struct WithinUlpsMatcher : MatcherBase { + WithinUlpsMatcher(double target, int ulps, FloatingPointKind baseType); + bool match(double const& matchee) const override; + std::string describe() const override; + private: + double m_target; + int m_ulps; + FloatingPointKind m_type; + }; + + + } // namespace Floating + + Floating::WithinUlpsMatcher WithinULP(double target, int maxUlpDiff); + Floating::WithinUlpsMatcher WithinULP(float target, int maxUlpDiff); + Floating::WithinAbsMatcher WithinAbs(double target, double margin); + + // The following functions create the actual matcher objects. + // This allows the types to be inferred + +// StdString::EqualsMatcher Equals( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ); +// StdString::ContainsMatcher Contains( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ); +// StdString::EndsWithMatcher EndsWith( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ); +// StdString::StartsWithMatcher StartsWith( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ); + +} // namespace Matchers +} // namespace Catch + +#endif // TWOBLUECUBES_CATCH_MATCHERS_FLOATING_H_INCLUDED diff --git a/projects/SelfTest/Baselines/console.std.approved.txt b/projects/SelfTest/Baselines/console.std.approved.txt index f0791caf..f944b6ad 100644 --- a/projects/SelfTest/Baselines/console.std.approved.txt +++ b/projects/SelfTest/Baselines/console.std.approved.txt @@ -1003,6 +1003,6 @@ with expansion: "{?}" == "1" =============================================================================== -test cases: 185 | 134 passed | 47 failed | 4 failed as expected -assertions: 904 | 787 passed | 96 failed | 21 failed as expected +test cases: 187 | 136 passed | 47 failed | 4 failed as expected +assertions: 935 | 818 passed | 96 failed | 21 failed as expected diff --git a/projects/SelfTest/Baselines/console.sw.approved.txt b/projects/SelfTest/Baselines/console.sw.approved.txt index 2c205275..ead39b33 100644 --- a/projects/SelfTest/Baselines/console.sw.approved.txt +++ b/projects/SelfTest/Baselines/console.sw.approved.txt @@ -1717,6 +1717,234 @@ PASSED: with expansion: 3628800 (0x) == 3628800 (0x) +------------------------------------------------------------------------------- +Floating point matchers: double + Margin +------------------------------------------------------------------------------- +MatchersTests.cpp: +............................................................................... + +MatchersTests.cpp:: +PASSED: + REQUIRE_THAT( 1., WithinAbs(1., 0) ) +with expansion: + 1.0 is within 0.000000 of 1.000000 + +MatchersTests.cpp:: +PASSED: + REQUIRE_THAT( 0., WithinAbs(1., 1) ) +with expansion: + 0.0 is within 1.000000 of 1.000000 + +MatchersTests.cpp:: +PASSED: + REQUIRE_THAT( 0., !WithinAbs(1., 0.99) ) +with expansion: + 0.0 not is within 0.990000 of 1.000000 + +MatchersTests.cpp:: +PASSED: + REQUIRE_THAT( 0., !WithinAbs(1., 0.99) ) +with expansion: + 0.0 not is within 0.990000 of 1.000000 + +MatchersTests.cpp:: +PASSED: + REQUIRE_THAT( NAN, !WithinAbs(NAN, 0) ) +with expansion: + nanf not is within 0.000000 of nan + +------------------------------------------------------------------------------- +Floating point matchers: double + ULPs +------------------------------------------------------------------------------- +MatchersTests.cpp: +............................................................................... + +MatchersTests.cpp:: +PASSED: + REQUIRE_THAT( 1., WithinULP(1., 0) ) +with expansion: + 1.0 is within 0 ULPs of 1.000000 + +MatchersTests.cpp:: +PASSED: + REQUIRE_THAT( std::nextafter(1., 2.), WithinULP(1., 1) ) +with expansion: + 1.0 is within 1 ULPs of 1.000000 + +MatchersTests.cpp:: +PASSED: + REQUIRE_THAT( std::nextafter(1., 0.), WithinULP(1., 1) ) +with expansion: + 1.0 is within 1 ULPs of 1.000000 + +MatchersTests.cpp:: +PASSED: + REQUIRE_THAT( std::nextafter(1., 2.), !WithinULP(1., 0) ) +with expansion: + 1.0 not is within 0 ULPs of 1.000000 + +MatchersTests.cpp:: +PASSED: + REQUIRE_THAT( 1., WithinULP(1., 0) ) +with expansion: + 1.0 is within 0 ULPs of 1.000000 + +MatchersTests.cpp:: +PASSED: + REQUIRE_THAT( -0., WithinULP(0., 0) ) +with expansion: + -0.0 is within 0 ULPs of 0.000000 + +MatchersTests.cpp:: +PASSED: + REQUIRE_THAT( NAN, !WithinULP(NAN, 123) ) +with expansion: + nanf not is within 123 ULPs of nanf + +------------------------------------------------------------------------------- +Floating point matchers: double + Composed +------------------------------------------------------------------------------- +MatchersTests.cpp: +............................................................................... + +MatchersTests.cpp:: +PASSED: + REQUIRE_THAT( 1., WithinAbs(1., 0.5) || WithinULP(2., 1) ) +with expansion: + 1.0 ( is within 0.500000 of 1.000000 or is within 1 ULPs of 2.000000 ) + +MatchersTests.cpp:: +PASSED: + REQUIRE_THAT( 1., WithinAbs(2., 0.5) || WithinULP(1., 0) ) +with expansion: + 1.0 ( is within 0.500000 of 2.000000 or is within 0 ULPs of 1.000000 ) + +MatchersTests.cpp:: +PASSED: + REQUIRE_THAT( NAN, !(WithinAbs(NAN, 100) || WithinULP(NAN, 123)) ) +with expansion: + nanf not ( is within 100.000000 of nan or is within 123 ULPs of nanf ) + +------------------------------------------------------------------------------- +Floating point matchers: float + Margin +------------------------------------------------------------------------------- +MatchersTests.cpp: +............................................................................... + +MatchersTests.cpp:: +PASSED: + REQUIRE_THAT( 1.f, WithinAbs(1.f, 0) ) +with expansion: + 1.0f is within 0.000000 of 1.000000 + +MatchersTests.cpp:: +PASSED: + REQUIRE_THAT( 0.f, WithinAbs(1.f, 1) ) +with expansion: + 0.0f is within 1.000000 of 1.000000 + +MatchersTests.cpp:: +PASSED: + REQUIRE_THAT( 0.f, !WithinAbs(1.f, 0.99f) ) +with expansion: + 0.0f not is within 0.990000 of 1.000000 + +MatchersTests.cpp:: +PASSED: + REQUIRE_THAT( 0.f, !WithinAbs(1.f, 0.99f) ) +with expansion: + 0.0f not is within 0.990000 of 1.000000 + +MatchersTests.cpp:: +PASSED: + REQUIRE_THAT( 0.f, WithinAbs(-0.f, 0) ) +with expansion: + 0.0f is within 0.000000 of -0.000000 + +MatchersTests.cpp:: +PASSED: + REQUIRE_THAT( NAN, !WithinAbs(NAN, 0) ) +with expansion: + nanf not is within 0.000000 of nan + +------------------------------------------------------------------------------- +Floating point matchers: float + ULPs +------------------------------------------------------------------------------- +MatchersTests.cpp: +............................................................................... + +MatchersTests.cpp:: +PASSED: + REQUIRE_THAT( 1.f, WithinULP(1.f, 0) ) +with expansion: + 1.0f is within 0 ULPs of 1.000000f + +MatchersTests.cpp:: +PASSED: + REQUIRE_THAT( std::nextafter(1.f, 2.f), WithinULP(1.f, 1) ) +with expansion: + 1.0f is within 1 ULPs of 1.000000f + +MatchersTests.cpp:: +PASSED: + REQUIRE_THAT( std::nextafter(1.f, 0.f), WithinULP(1.f, 1) ) +with expansion: + 1.0f is within 1 ULPs of 1.000000f + +MatchersTests.cpp:: +PASSED: + REQUIRE_THAT( std::nextafter(1.f, 2.f), !WithinULP(1.f, 0) ) +with expansion: + 1.0f not is within 0 ULPs of 1.000000f + +MatchersTests.cpp:: +PASSED: + REQUIRE_THAT( 1.f, WithinULP(1.f, 0) ) +with expansion: + 1.0f is within 0 ULPs of 1.000000f + +MatchersTests.cpp:: +PASSED: + REQUIRE_THAT( -0.f, WithinULP(0.f, 0) ) +with expansion: + -0.0f is within 0 ULPs of 0.000000f + +MatchersTests.cpp:: +PASSED: + REQUIRE_THAT( NAN, !WithinULP(NAN, 123) ) +with expansion: + nanf not is within 123 ULPs of nanf + +------------------------------------------------------------------------------- +Floating point matchers: float + Composed +------------------------------------------------------------------------------- +MatchersTests.cpp: +............................................................................... + +MatchersTests.cpp:: +PASSED: + REQUIRE_THAT( 1.f, WithinAbs(1.f, 0.5) || WithinULP(1.f, 1) ) +with expansion: + 1.0f ( is within 0.500000 of 1.000000 or is within 1 ULPs of 1.000000f ) + +MatchersTests.cpp:: +PASSED: + REQUIRE_THAT( 1.f, WithinAbs(2.f, 0.5) || WithinULP(1.f, 0) ) +with expansion: + 1.0f ( is within 0.500000 of 2.000000 or is within 0 ULPs of 1.000000f ) + +MatchersTests.cpp:: +PASSED: + REQUIRE_THAT( NAN, !(WithinAbs(NAN, 100) || WithinULP(NAN, 123)) ) +with expansion: + nanf not ( is within 100.000000 of nan or is within 123 ULPs of nanf ) + ------------------------------------------------------------------------------- Greater-than inequalities with different epsilons ------------------------------------------------------------------------------- @@ -7632,6 +7860,6 @@ MiscTests.cpp:: PASSED: =============================================================================== -test cases: 185 | 132 passed | 49 failed | 4 failed as expected -assertions: 903 | 783 passed | 99 failed | 21 failed as expected +test cases: 187 | 134 passed | 49 failed | 4 failed as expected +assertions: 934 | 814 passed | 99 failed | 21 failed as expected diff --git a/projects/SelfTest/Baselines/junit.sw.approved.txt b/projects/SelfTest/Baselines/junit.sw.approved.txt index 0aba9725..a58c16ea 100644 --- a/projects/SelfTest/Baselines/junit.sw.approved.txt +++ b/projects/SelfTest/Baselines/junit.sw.approved.txt @@ -1,7 +1,7 @@ - + @@ -263,6 +263,12 @@ MessageTests.cpp: + + + + + + diff --git a/projects/SelfTest/Baselines/xml.sw.approved.txt b/projects/SelfTest/Baselines/xml.sw.approved.txt index d3ef33c9..f8abb183 100644 --- a/projects/SelfTest/Baselines/xml.sw.approved.txt +++ b/projects/SelfTest/Baselines/xml.sw.approved.txt @@ -1945,6 +1945,278 @@ + +
+ + + 1., WithinAbs(1., 0) + + + 1.0 is within 0.000000 of 1.000000 + + + + + 0., WithinAbs(1., 1) + + + 0.0 is within 1.000000 of 1.000000 + + + + + 0., !WithinAbs(1., 0.99) + + + 0.0 not is within 0.990000 of 1.000000 + + + + + 0., !WithinAbs(1., 0.99) + + + 0.0 not is within 0.990000 of 1.000000 + + + + + NAN, !WithinAbs(NAN, 0) + + + nanf not is within 0.000000 of nan + + + +
+
+ + + 1., WithinULP(1., 0) + + + 1.0 is within 0 ULPs of 1.000000 + + + + + std::nextafter(1., 2.), WithinULP(1., 1) + + + 1.0 is within 1 ULPs of 1.000000 + + + + + std::nextafter(1., 0.), WithinULP(1., 1) + + + 1.0 is within 1 ULPs of 1.000000 + + + + + std::nextafter(1., 2.), !WithinULP(1., 0) + + + 1.0 not is within 0 ULPs of 1.000000 + + + + + 1., WithinULP(1., 0) + + + 1.0 is within 0 ULPs of 1.000000 + + + + + -0., WithinULP(0., 0) + + + -0.0 is within 0 ULPs of 0.000000 + + + + + NAN, !WithinULP(NAN, 123) + + + nanf not is within 123 ULPs of nanf + + + +
+
+ + + 1., WithinAbs(1., 0.5) || WithinULP(2., 1) + + + 1.0 ( is within 0.500000 of 1.000000 or is within 1 ULPs of 2.000000 ) + + + + + 1., WithinAbs(2., 0.5) || WithinULP(1., 0) + + + 1.0 ( is within 0.500000 of 2.000000 or is within 0 ULPs of 1.000000 ) + + + + + NAN, !(WithinAbs(NAN, 100) || WithinULP(NAN, 123)) + + + nanf not ( is within 100.000000 of nan or is within 123 ULPs of nanf ) + + + +
+ +
+ +
+ + + 1.f, WithinAbs(1.f, 0) + + + 1.0f is within 0.000000 of 1.000000 + + + + + 0.f, WithinAbs(1.f, 1) + + + 0.0f is within 1.000000 of 1.000000 + + + + + 0.f, !WithinAbs(1.f, 0.99f) + + + 0.0f not is within 0.990000 of 1.000000 + + + + + 0.f, !WithinAbs(1.f, 0.99f) + + + 0.0f not is within 0.990000 of 1.000000 + + + + + 0.f, WithinAbs(-0.f, 0) + + + 0.0f is within 0.000000 of -0.000000 + + + + + NAN, !WithinAbs(NAN, 0) + + + nanf not is within 0.000000 of nan + + + +
+
+ + + 1.f, WithinULP(1.f, 0) + + + 1.0f is within 0 ULPs of 1.000000f + + + + + std::nextafter(1.f, 2.f), WithinULP(1.f, 1) + + + 1.0f is within 1 ULPs of 1.000000f + + + + + std::nextafter(1.f, 0.f), WithinULP(1.f, 1) + + + 1.0f is within 1 ULPs of 1.000000f + + + + + std::nextafter(1.f, 2.f), !WithinULP(1.f, 0) + + + 1.0f not is within 0 ULPs of 1.000000f + + + + + 1.f, WithinULP(1.f, 0) + + + 1.0f is within 0 ULPs of 1.000000f + + + + + -0.f, WithinULP(0.f, 0) + + + -0.0f is within 0 ULPs of 0.000000f + + + + + NAN, !WithinULP(NAN, 123) + + + nanf not is within 123 ULPs of nanf + + + +
+
+ + + 1.f, WithinAbs(1.f, 0.5) || WithinULP(1.f, 1) + + + 1.0f ( is within 0.500000 of 1.000000 or is within 1 ULPs of 1.000000f ) + + + + + 1.f, WithinAbs(2.f, 0.5) || WithinULP(1.f, 0) + + + 1.0f ( is within 0.500000 of 2.000000 or is within 0 ULPs of 1.000000f ) + + + + + NAN, !(WithinAbs(NAN, 100) || WithinULP(NAN, 123)) + + + nanf not ( is within 100.000000 of nan or is within 123 ULPs of nanf ) + + + +
+ +
@@ -8446,7 +8718,7 @@ loose text artifact - + - + diff --git a/projects/SelfTest/MatchersTests.cpp b/projects/SelfTest/MatchersTests.cpp index 8f376292..ce4ea0cd 100644 --- a/projects/SelfTest/MatchersTests.cpp +++ b/projects/SelfTest/MatchersTests.cpp @@ -226,6 +226,67 @@ TEST_CASE("Exception matchers that fail", "[matchers][exceptions][!throws][.fail } } +TEST_CASE("Floating point matchers: float", "[matchers][floating-point]") { + SECTION("Margin") { + REQUIRE_THAT(1.f, WithinAbs(1.f, 0)); + REQUIRE_THAT(0.f, WithinAbs(1.f, 1)); + + REQUIRE_THAT(0.f, !WithinAbs(1.f, 0.99f)); + REQUIRE_THAT(0.f, !WithinAbs(1.f, 0.99f)); + + REQUIRE_THAT(0.f, WithinAbs(-0.f, 0)); + REQUIRE_THAT(NAN, !WithinAbs(NAN, 0)); + } + SECTION("ULPs") { + REQUIRE_THAT(1.f, WithinULP(1.f, 0)); + + REQUIRE_THAT(std::nextafter(1.f, 2.f), WithinULP(1.f, 1)); + REQUIRE_THAT(std::nextafter(1.f, 0.f), WithinULP(1.f, 1)); + REQUIRE_THAT(std::nextafter(1.f, 2.f), !WithinULP(1.f, 0)); + + REQUIRE_THAT(1.f, WithinULP(1.f, 0)); + REQUIRE_THAT(-0.f, WithinULP(0.f, 0)); + + REQUIRE_THAT(NAN, !WithinULP(NAN, 123)); + } + 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(NAN, !(WithinAbs(NAN, 100) || WithinULP(NAN, 123))); + } +} + +TEST_CASE("Floating point matchers: double", "[matchers][floating-point]") { + SECTION("Margin") { + REQUIRE_THAT(1., WithinAbs(1., 0)); + REQUIRE_THAT(0., WithinAbs(1., 1)); + + REQUIRE_THAT(0., !WithinAbs(1., 0.99)); + REQUIRE_THAT(0., !WithinAbs(1., 0.99)); + + REQUIRE_THAT(NAN, !WithinAbs(NAN, 0)); + } + SECTION("ULPs") { + REQUIRE_THAT(1., WithinULP(1., 0)); + + REQUIRE_THAT(std::nextafter(1., 2.), WithinULP(1., 1)); + REQUIRE_THAT(std::nextafter(1., 0.), WithinULP(1., 1)); + REQUIRE_THAT(std::nextafter(1., 2.), !WithinULP(1., 0)); + + REQUIRE_THAT(1., WithinULP(1., 0)); + REQUIRE_THAT(-0., WithinULP(0., 0)); + + REQUIRE_THAT(NAN, !WithinULP(NAN, 123)); + } + SECTION("Composed") { + REQUIRE_THAT(1., WithinAbs(1., 0.5) || WithinULP(2., 1)); + REQUIRE_THAT(1., WithinAbs(2., 0.5) || WithinULP(1., 0)); + + REQUIRE_THAT(NAN, !(WithinAbs(NAN, 100) || WithinULP(NAN, 123))); + } +} + #endif // CATCH_CONFIG_DISABLE_MATCHERS #ifdef __clang__ diff --git a/scripts/approvalTests.py b/scripts/approvalTests.py index 8194c422..04a7ad57 100755 --- a/scripts/approvalTests.py +++ b/scripts/approvalTests.py @@ -52,6 +52,12 @@ infParser = re.compile(r''' | __builtin_huge_valf\(\) # OSX macro ''', re.VERBOSE) +nanParser = re.compile(r''' + \(\(float\)\(\(\(float\)\(1e\+300\ \*\ 1e\+300\)\)\ \*\ 0\.0F\)\) # MSVC NAN macro + | + \(__builtin_nanf\ \(""\)\) # Linux (ubuntu) NAN macro +''', re.VERBOSE) + if len(sys.argv) == 2: cmdPath = sys.argv[1] @@ -110,6 +116,7 @@ def filterLine(line): line = errnoParser.sub('errno', line) line = sinceEpochParser.sub('{since-epoch-report}', line) line = infParser.sub('INFINITY', line) + line = nanParser.sub('NAN', line) return line