Allow full range of target ULP values for the ULPMatcher

Previously it was limited to roughly 2 billion ULPs, rather than
the roughly 2^64 possible ones.
This commit is contained in:
Martin Hořeňovský 2019-10-05 13:23:14 +02:00
parent ebc5609484
commit c38a5caa2e
No known key found for this signature in database
GPG Key ID: DE48307B8B0D381A
8 changed files with 34 additions and 58 deletions

View File

@ -34,34 +34,22 @@ enum class FloatingPointKind : uint8_t {
namespace { namespace {
template <typename T> int32_t convert(float f) {
struct Converter;
template <>
struct Converter<float> {
static_assert(sizeof(float) == sizeof(int32_t), "Important ULP matcher assumption violated"); static_assert(sizeof(float) == sizeof(int32_t), "Important ULP matcher assumption violated");
Converter(float f) {
std::memcpy(&i, &f, sizeof(f));
}
int32_t i; int32_t i;
}; std::memcpy(&i, &f, sizeof(f));
return i;
}
template <> int64_t convert(double d) {
struct Converter<double> {
static_assert(sizeof(double) == sizeof(int64_t), "Important ULP matcher assumption violated"); static_assert(sizeof(double) == sizeof(int64_t), "Important ULP matcher assumption violated");
Converter(double d) {
std::memcpy(&i, &d, sizeof(d));
}
int64_t i; int64_t i;
}; std::memcpy(&i, &d, sizeof(d));
return i;
template <typename T>
auto convert(T t) -> Converter<T> {
return Converter<T>(t);
} }
template <typename FP> template <typename FP>
bool almostEqualUlps(FP lhs, FP rhs, int maxUlpDiff) { bool almostEqualUlps(FP lhs, FP rhs, uint64_t maxUlpDiff) {
// Comparison with NaN should always be false. // Comparison with NaN should always be false.
// This way we can rule it out before getting into the ugly details // This way we can rule it out before getting into the ugly details
if (Catch::isnan(lhs) || Catch::isnan(rhs)) { if (Catch::isnan(lhs) || Catch::isnan(rhs)) {
@ -71,13 +59,13 @@ bool almostEqualUlps(FP lhs, FP rhs, int maxUlpDiff) {
auto lc = convert(lhs); auto lc = convert(lhs);
auto rc = convert(rhs); auto rc = convert(rhs);
if ((lc.i < 0) != (rc.i < 0)) { if ((lc < 0) != (rc < 0)) {
// Potentially we can have +0 and -0 // Potentially we can have +0 and -0
return lhs == rhs; return lhs == rhs;
} }
auto ulpDiff = std::abs(lc.i - rc.i); auto ulpDiff = std::abs(lc - rc);
return ulpDiff <= maxUlpDiff; return static_cast<uint64_t>(ulpDiff) <= maxUlpDiff;
} }
#if defined(CATCH_CONFIG_GLOBAL_NEXTAFTER) #if defined(CATCH_CONFIG_GLOBAL_NEXTAFTER)
@ -99,8 +87,8 @@ namespace Catch {
#endif #endif
template <typename FP> template <typename FP>
FP step(FP start, FP direction, int steps) { FP step(FP start, FP direction, uint64_t steps) {
for (int i = 0; i < steps; ++i) { for (uint64_t i = 0; i < steps; ++i) {
#if defined(CATCH_CONFIG_GLOBAL_NEXTAFTER) #if defined(CATCH_CONFIG_GLOBAL_NEXTAFTER)
start = Catch::nextafter(start, direction); start = Catch::nextafter(start, direction);
#else #else
@ -133,10 +121,11 @@ namespace Floating {
} }
WithinUlpsMatcher::WithinUlpsMatcher(double target, int ulps, FloatingPointKind baseType) WithinUlpsMatcher::WithinUlpsMatcher(double target, uint64_t ulps, FloatingPointKind baseType)
:m_target{ target }, m_ulps{ ulps }, m_type{ baseType } { :m_target{ target }, m_ulps{ ulps }, m_type{ baseType } {
CATCH_ENFORCE(ulps >= 0, "Invalid ULP setting: " << ulps << '.' CATCH_ENFORCE(m_type == FloatingPointKind::Double
<< " ULPs have to be non-negative."); || m_ulps < std::numeric_limits<uint32_t>::max(),
"Provided ULP is impossibly large for a float comparison.");
} }
#if defined(__clang__) #if defined(__clang__)
@ -190,11 +179,11 @@ namespace Floating {
Floating::WithinUlpsMatcher WithinULP(double target, int maxUlpDiff) { Floating::WithinUlpsMatcher WithinULP(double target, uint64_t maxUlpDiff) {
return Floating::WithinUlpsMatcher(target, maxUlpDiff, Floating::FloatingPointKind::Double); return Floating::WithinUlpsMatcher(target, maxUlpDiff, Floating::FloatingPointKind::Double);
} }
Floating::WithinUlpsMatcher WithinULP(float target, int maxUlpDiff) { Floating::WithinUlpsMatcher WithinULP(float target, uint64_t maxUlpDiff) {
return Floating::WithinUlpsMatcher(target, maxUlpDiff, Floating::FloatingPointKind::Float); return Floating::WithinUlpsMatcher(target, maxUlpDiff, Floating::FloatingPointKind::Float);
} }

View File

@ -26,12 +26,12 @@ namespace Matchers {
}; };
struct WithinUlpsMatcher : MatcherBase<double> { struct WithinUlpsMatcher : MatcherBase<double> {
WithinUlpsMatcher(double target, int ulps, FloatingPointKind baseType); WithinUlpsMatcher(double target, uint64_t ulps, FloatingPointKind baseType);
bool match(double const& matchee) const override; bool match(double const& matchee) const override;
std::string describe() const override; std::string describe() const override;
private: private:
double m_target; double m_target;
int m_ulps; uint64_t m_ulps;
FloatingPointKind m_type; FloatingPointKind m_type;
}; };
@ -40,8 +40,8 @@ namespace Matchers {
// The following functions create the actual matcher objects. // The following functions create the actual matcher objects.
// This allows the types to be inferred // This allows the types to be inferred
Floating::WithinUlpsMatcher WithinULP(double target, int maxUlpDiff); Floating::WithinUlpsMatcher WithinULP(double target, uint64_t maxUlpDiff);
Floating::WithinUlpsMatcher WithinULP(float target, int maxUlpDiff); Floating::WithinUlpsMatcher WithinULP(float target, uint64_t maxUlpDiff);
Floating::WithinAbsMatcher WithinAbs(double target, double margin); Floating::WithinAbsMatcher WithinAbs(double target, double margin);
} // namespace Matchers } // namespace Matchers

View File

@ -408,7 +408,6 @@ Matchers.tests.cpp:<line number>: passed: 1., WithinAbs(2., 0.5) || WithinULP(1.
Matchers.tests.cpp:<line number>: passed: WithinAbs(1., 0.) Matchers.tests.cpp:<line number>: passed: WithinAbs(1., 0.)
Matchers.tests.cpp:<line number>: passed: WithinAbs(1., -1.), std::domain_error Matchers.tests.cpp:<line number>: passed: WithinAbs(1., -1.), std::domain_error
Matchers.tests.cpp:<line number>: passed: WithinULP(1., 0) Matchers.tests.cpp:<line number>: passed: WithinULP(1., 0)
Matchers.tests.cpp:<line number>: passed: WithinULP(1., -1), std::domain_error
Matchers.tests.cpp:<line number>: passed: 1.f, WithinAbs(1.f, 0) for: 1.0f is within 0.0 of 1.0 Matchers.tests.cpp:<line number>: passed: 1.f, WithinAbs(1.f, 0) for: 1.0f is within 0.0 of 1.0
Matchers.tests.cpp:<line number>: passed: 0.f, WithinAbs(1.f, 1) for: 0.0f is within 1.0 of 1.0 Matchers.tests.cpp:<line number>: passed: 0.f, WithinAbs(1.f, 1) for: 0.0f is within 1.0 of 1.0
Matchers.tests.cpp:<line number>: passed: 0.f, !WithinAbs(1.f, 0.99f) for: 0.0f not is within 0.9900000095 of 1.0 Matchers.tests.cpp:<line number>: passed: 0.f, !WithinAbs(1.f, 0.99f) for: 0.0f not is within 0.9900000095 of 1.0
@ -429,7 +428,7 @@ Matchers.tests.cpp:<line number>: passed: 1.f, WithinAbs(2.f, 0.5) || WithinULP(
Matchers.tests.cpp:<line number>: passed: WithinAbs(1.f, 0.f) Matchers.tests.cpp:<line number>: passed: WithinAbs(1.f, 0.f)
Matchers.tests.cpp:<line number>: passed: WithinAbs(1.f, -1.f), std::domain_error Matchers.tests.cpp:<line number>: passed: WithinAbs(1.f, -1.f), std::domain_error
Matchers.tests.cpp:<line number>: passed: WithinULP(1.f, 0) Matchers.tests.cpp:<line number>: passed: WithinULP(1.f, 0)
Matchers.tests.cpp:<line number>: passed: WithinULP(1.f, -1), std::domain_error Matchers.tests.cpp:<line number>: passed: WithinULP(1.f, static_cast<uint64_t>(-1)), std::domain_error
Generators.tests.cpp:<line number>: passed: i % 2 == 0 for: 0 == 0 Generators.tests.cpp:<line number>: passed: i % 2 == 0 for: 0 == 0
Generators.tests.cpp:<line number>: passed: i % 2 == 0 for: 0 == 0 Generators.tests.cpp:<line number>: passed: i % 2 == 0 for: 0 == 0
Generators.tests.cpp:<line number>: passed: i % 2 == 0 for: 0 == 0 Generators.tests.cpp:<line number>: passed: i % 2 == 0 for: 0 == 0

View File

@ -1381,5 +1381,5 @@ due to unexpected exception with message:
=============================================================================== ===============================================================================
test cases: 300 | 226 passed | 70 failed | 4 failed as expected test cases: 300 | 226 passed | 70 failed | 4 failed as expected
assertions: 1565 | 1413 passed | 131 failed | 21 failed as expected assertions: 1564 | 1412 passed | 131 failed | 21 failed as expected

View File

@ -3020,9 +3020,6 @@ Matchers.tests.cpp:<line number>: PASSED:
Matchers.tests.cpp:<line number>: PASSED: Matchers.tests.cpp:<line number>: PASSED:
REQUIRE_NOTHROW( WithinULP(1., 0) ) REQUIRE_NOTHROW( WithinULP(1., 0) )
Matchers.tests.cpp:<line number>: PASSED:
REQUIRE_THROWS_AS( WithinULP(1., -1), std::domain_error )
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
Floating point matchers: float Floating point matchers: float
Margin Margin
@ -3149,7 +3146,7 @@ Matchers.tests.cpp:<line number>: PASSED:
REQUIRE_NOTHROW( WithinULP(1.f, 0) ) REQUIRE_NOTHROW( WithinULP(1.f, 0) )
Matchers.tests.cpp:<line number>: PASSED: Matchers.tests.cpp:<line number>: PASSED:
REQUIRE_THROWS_AS( WithinULP(1.f, -1), std::domain_error ) REQUIRE_THROWS_AS( WithinULP(1.f, static_cast<uint64_t>(-1)), std::domain_error )
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
Generators -- adapters Generators -- adapters
@ -12503,5 +12500,5 @@ Misc.tests.cpp:<line number>: PASSED:
=============================================================================== ===============================================================================
test cases: 300 | 210 passed | 86 failed | 4 failed as expected test cases: 300 | 210 passed | 86 failed | 4 failed as expected
assertions: 1582 | 1413 passed | 148 failed | 21 failed as expected assertions: 1581 | 1412 passed | 148 failed | 21 failed as expected

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<testsuitesloose text artifact <testsuitesloose text artifact
> >
<testsuite name="<exe-name>" errors="17" failures="132" tests="1583" hostname="tbd" time="{duration}" timestamp="{iso8601-timestamp}"> <testsuite name="<exe-name>" errors="17" failures="132" tests="1582" hostname="tbd" time="{duration}" timestamp="{iso8601-timestamp}">
<properties> <properties>
<property name="filters" value="~[!nonportable]~[!benchmark]~[approvals]"/> <property name="filters" value="~[!nonportable]~[!benchmark]~[approvals]"/>
<property name="random-seed" value="1"/> <property name="random-seed" value="1"/>

View File

@ -3630,15 +3630,7 @@ Nor would this
WithinULP(1., 0) WithinULP(1., 0)
</Expanded> </Expanded>
</Expression> </Expression>
<Expression success="true" type="REQUIRE_THROWS_AS" filename="projects/<exe-name>/UsageTests/Matchers.tests.cpp" > <OverallResults successes="3" failures="0" expectedFailures="0"/>
<Original>
WithinULP(1., -1), std::domain_error
</Original>
<Expanded>
WithinULP(1., -1), std::domain_error
</Expanded>
</Expression>
<OverallResults successes="4" failures="0" expectedFailures="0"/>
</Section> </Section>
<OverallResult success="true"/> <OverallResult success="true"/>
</TestCase> </TestCase>
@ -3815,10 +3807,10 @@ Nor would this
</Expression> </Expression>
<Expression success="true" type="REQUIRE_THROWS_AS" filename="projects/<exe-name>/UsageTests/Matchers.tests.cpp" > <Expression success="true" type="REQUIRE_THROWS_AS" filename="projects/<exe-name>/UsageTests/Matchers.tests.cpp" >
<Original> <Original>
WithinULP(1.f, -1), std::domain_error WithinULP(1.f, static_cast&lt;uint64_t>(-1)), std::domain_error
</Original> </Original>
<Expanded> <Expanded>
WithinULP(1.f, -1), std::domain_error WithinULP(1.f, static_cast&lt;uint64_t>(-1)), std::domain_error
</Expanded> </Expanded>
</Expression> </Expression>
<OverallResults successes="4" failures="0" expectedFailures="0"/> <OverallResults successes="4" failures="0" expectedFailures="0"/>
@ -14880,7 +14872,7 @@ loose text artifact
</Section> </Section>
<OverallResult success="true"/> <OverallResult success="true"/>
</TestCase> </TestCase>
<OverallResults successes="1413" failures="149" expectedFailures="21"/> <OverallResults successes="1412" failures="149" expectedFailures="21"/>
</Group> </Group>
<OverallResults successes="1413" failures="148" expectedFailures="21"/> <OverallResults successes="1412" failures="148" expectedFailures="21"/>
</Catch> </Catch>

View File

@ -372,7 +372,7 @@ namespace { namespace MatchersTests {
REQUIRE_THROWS_AS(WithinAbs(1.f, -1.f), std::domain_error); REQUIRE_THROWS_AS(WithinAbs(1.f, -1.f), std::domain_error);
REQUIRE_NOTHROW(WithinULP(1.f, 0)); REQUIRE_NOTHROW(WithinULP(1.f, 0));
REQUIRE_THROWS_AS(WithinULP(1.f, -1), std::domain_error); REQUIRE_THROWS_AS(WithinULP(1.f, static_cast<uint64_t>(-1)), std::domain_error);
} }
} }
@ -408,7 +408,6 @@ namespace { namespace MatchersTests {
REQUIRE_THROWS_AS(WithinAbs(1., -1.), std::domain_error); REQUIRE_THROWS_AS(WithinAbs(1., -1.), std::domain_error);
REQUIRE_NOTHROW(WithinULP(1., 0)); REQUIRE_NOTHROW(WithinULP(1., 0));
REQUIRE_THROWS_AS(WithinULP(1., -1), std::domain_error);
} }
} }