Fix ulp distance calculation for numbers with different signs

This is a simplification of the fix proposed in #2152, with the
critical function split out so that it can be tested directly,
without having to go through the ULP matcher.

Closes #2152
This commit is contained in:
Martin Hořeňovský 2021-07-26 22:01:04 +02:00
parent 6f21a3609c
commit 3d1cf95b32
No known key found for this signature in database
GPG Key ID: DE48307B8B0D381A
19 changed files with 502 additions and 38 deletions

View File

@ -161,6 +161,11 @@ away from the `target` value. The short version of what this means
is that there is no more than `maxUlpDiff - 1` representeable floating
point numbers between the argument for matching and the `target` value.
**Important**: The WithinULP matcher requires the platform to use the
[IEEE-754](https://en.wikipedia.org/wiki/IEEE_754) representation for
floating point numbers.
`WithinRel` creates a matcher that accepts floating point numbers that
are _approximately equal_ with the `target` with tolerance of `eps.`
Specifically, it matches if

View File

@ -65,6 +65,7 @@ set(INTERNAL_HEADERS
${SOURCES_DIR}/internal/catch_errno_guard.hpp
${SOURCES_DIR}/internal/catch_exception_translator_registry.hpp
${SOURCES_DIR}/internal/catch_fatal_condition_handler.hpp
${SOURCES_DIR}/internal/catch_floating_point_helpers.hpp
${SOURCES_DIR}/internal/catch_unique_name.hpp
${SOURCES_DIR}/generators/catch_generator_exception.hpp
${SOURCES_DIR}/generators/catch_generators.hpp
@ -160,6 +161,7 @@ set(IMPL_SOURCES
${SOURCES_DIR}/internal/catch_enum_values_registry.cpp
${SOURCES_DIR}/internal/catch_exception_translator_registry.cpp
${SOURCES_DIR}/internal/catch_fatal_condition_handler.cpp
${SOURCES_DIR}/internal/catch_floating_point_helpers.cpp
${SOURCES_DIR}/generators/internal/catch_generators_combined_tu.cpp
${SOURCES_DIR}/interfaces/catch_interfaces_combined_tu.cpp
${SOURCES_DIR}/interfaces/catch_interfaces_reporter.cpp

View File

@ -66,6 +66,7 @@
#include <catch2/internal/catch_errno_guard.hpp>
#include <catch2/internal/catch_exception_translator_registry.hpp>
#include <catch2/internal/catch_fatal_condition_handler.hpp>
#include <catch2/internal/catch_floating_point_helpers.hpp>
#include <catch2/internal/catch_lazy_expr.hpp>
#include <catch2/internal/catch_leak_detector.hpp>
#include <catch2/internal/catch_list.hpp>

View File

@ -0,0 +1,32 @@
// Copyright Catch2 Authors
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// https://www.boost.org/LICENSE_1_0.txt)
// SPDX-License-Identifier: BSL-1.0
#include <catch2/internal/catch_floating_point_helpers.hpp>
#include <cstring>
namespace Catch {
namespace Detail {
uint32_t convertToBits(float f) {
static_assert(sizeof(float) == sizeof(uint32_t), "Important ULP matcher assumption violated");
uint32_t i;
std::memcpy(&i, &f, sizeof(f));
return i;
}
uint64_t convertToBits(double d) {
static_assert(sizeof(double) == sizeof(uint64_t), "Important ULP matcher assumption violated");
uint64_t i;
std::memcpy(&i, &d, sizeof(d));
return i;
}
} // end namespace Detail
} // end namespace Catch

View File

@ -0,0 +1,88 @@
// Copyright Catch2 Authors
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// https://www.boost.org/LICENSE_1_0.txt)
// SPDX-License-Identifier: BSL-1.0
#ifndef CATCH_FLOATING_POINT_HELPERS_HPP_INCLUDED
#define CATCH_FLOATING_POINT_HELPERS_HPP_INCLUDED
#include <catch2/internal/catch_polyfills.hpp>
#include <cmath>
#include <cstdint>
#include <utility>
#include <limits>
namespace Catch {
namespace Detail {
uint32_t convertToBits(float f);
uint64_t convertToBits(double d);
} // end namespace Detail
/**
* Calculates the ULP distance between two floating point numbers
*
* The ULP distance of two floating point numbers is the count of
* valid floating point numbers representable between them.
*
* There are some exceptions between how this function counts the
* distance, and the interpretation of the standard as implemented.
* by e.g. `nextafter`. For this function it always holds that:
* * `(x == y) => ulpDistance(x, y) == 0` (so `ulpDistance(-0, 0) == 0`)
* * `ulpDistance(maxFinite, INF) == 1`
* * `ulpDistance(x, -x) == 2 * ulpDistance(x, 0)`
*
* \pre `!isnan( lhs )`
* \pre `!isnan( rhs )`
* \pre floating point numbers are represented in IEEE-754 format
*/
template <typename FP>
uint64_t ulpDistance( FP lhs, FP rhs ) {
assert( std::numeric_limits<FP>::is_iec559 &&
"ulpDistance assumes IEEE-754 format for floating point types" );
assert( !Catch::isnan( lhs ) &&
"Distance between NaN and number is not meaningful" );
assert( !Catch::isnan( rhs ) &&
"Distance between NaN and number is not meaningful" );
// We want X == Y to imply 0 ULP distance even if X and Y aren't
// bit-equal (-0 and 0), or X - Y != 0 (same sign infinities).
if ( lhs == rhs ) { return 0; }
// We need a properly typed positive zero for type inference.
static constexpr FP positive_zero{};
// We want to ensure that +/- 0 is always represented as positive zero
if ( lhs == positive_zero ) { lhs = positive_zero; }
if ( rhs == positive_zero ) { rhs = positive_zero; }
// If arguments have different signs, we can handle them by summing
// how far are they from 0 each.
if ( std::signbit( lhs ) != std::signbit( rhs ) ) {
return ulpDistance( std::abs( lhs ), positive_zero ) +
ulpDistance( std::abs( rhs ), positive_zero );
}
// When both lhs and rhs are of the same sign, we can just
// read the numbers bitwise as integers, and then subtract them
// (assuming IEEE).
uint64_t lc = Detail::convertToBits( lhs );
uint64_t rc = Detail::convertToBits( rhs );
// The ulp distance between two numbers is symmetric, so to avoid
// dealing with overflows we want the bigger converted number on the lhs
if ( lc < rc ) {
std::swap( lc, rc );
}
return lc - rc;
}
} // end namespace Catch
#endif // CATCH_FLOATING_POINT_HELPERS_HPP_INCLUDED

View File

@ -10,12 +10,12 @@
#include <catch2/internal/catch_polyfills.hpp>
#include <catch2/internal/catch_to_string.hpp>
#include <catch2/catch_tostring.hpp>
#include <catch2/internal/catch_floating_point_helpers.hpp>
#include <algorithm>
#include <cmath>
#include <cstdlib>
#include <cstdint>
#include <cstring>
#include <sstream>
#include <iomanip>
#include <limits>
@ -24,20 +24,6 @@
namespace Catch {
namespace {
int32_t convert(float f) {
static_assert(sizeof(float) == sizeof(int32_t), "Important ULP matcher assumption violated");
int32_t i;
std::memcpy(&i, &f, sizeof(f));
return i;
}
int64_t convert(double d) {
static_assert(sizeof(double) == sizeof(int64_t), "Important ULP matcher assumption violated");
int64_t i;
std::memcpy(&i, &d, sizeof(d));
return i;
}
template <typename FP>
bool almostEqualUlps(FP lhs, FP rhs, uint64_t maxUlpDiff) {
// Comparison with NaN should always be false.
@ -46,17 +32,10 @@ namespace {
return false;
}
auto lc = convert(lhs);
auto rc = convert(rhs);
// This should also handle positive and negative zeros, infinities
const auto ulpDist = ulpDistance(lhs, rhs);
if ((lc < 0) != (rc < 0)) {
// Potentially we can have +0 and -0
return lhs == rhs;
}
// static cast as a workaround for IBM XLC
auto ulpDiff = std::abs(static_cast<FP>(lc - rc));
return static_cast<uint64_t>(ulpDiff) <= maxUlpDiff;
return ulpDist <= maxUlpDiff;
}
#if defined(CATCH_CONFIG_GLOBAL_NEXTAFTER)
@ -131,6 +110,9 @@ namespace Detail {
CATCH_ENFORCE(m_type == Detail::FloatingPointKind::Double
|| m_ulps < (std::numeric_limits<uint32_t>::max)(),
"Provided ULP is impossibly large for a float comparison.");
CATCH_ENFORCE( std::numeric_limits<double>::is_iec559,
"WithinUlp matcher only supports platforms with "
"IEEE-754 compatible floating point representation" );
}
#if defined(__clang__)

View File

@ -80,6 +80,7 @@ set(TEST_SOURCES
${SELF_TEST_DIR}/IntrospectiveTests/Clara.tests.cpp
${SELF_TEST_DIR}/IntrospectiveTests/CmdLine.tests.cpp
${SELF_TEST_DIR}/IntrospectiveTests/Details.tests.cpp
${SELF_TEST_DIR}/IntrospectiveTests/FloatingPoint.tests.cpp
${SELF_TEST_DIR}/IntrospectiveTests/GeneratorsImpl.tests.cpp
${SELF_TEST_DIR}/IntrospectiveTests/InternalBenchmark.tests.cpp
${SELF_TEST_DIR}/IntrospectiveTests/PartTracker.tests.cpp

View File

@ -24,6 +24,8 @@ Nor would this
:test-result: PASS #1954 - 7 arg template test case sig compiles - 1, 1, 1, 1, 1, 0, 0
:test-result: PASS #1954 - 7 arg template test case sig compiles - 5, 1, 1, 1, 1, 0, 0
:test-result: PASS #1954 - 7 arg template test case sig compiles - 5, 3, 1, 1, 1, 0, 0
:test-result: PASS #2152 - ULP checks between differently signed values were wrong - double
:test-result: PASS #2152 - ULP checks between differently signed values were wrong - float
:test-result: XFAIL #748 - captures with unexpected exceptions
:test-result: PASS #809
:test-result: PASS #833
@ -282,6 +284,7 @@ Message from section two
:test-result: PASS classify_outliers
:test-result: PASS comparisons between const int variables
:test-result: PASS comparisons between int variables
:test-result: PASS convertToBits
:test-result: PASS empty tags are not allowed
:test-result: PASS erfc_inv
:test-result: PASS estimate_clock_resolution

View File

@ -80,6 +80,10 @@ PartTracker.tests.cpp:<line number>: passed: n for: 3
Misc.tests.cpp:<line number>: passed:
Misc.tests.cpp:<line number>: passed:
Misc.tests.cpp:<line number>: passed:
Matchers.tests.cpp:<line number>: passed: smallest_non_zero, WithinULP( -smallest_non_zero, 2 ) for: 0.0 is within 2 ULPs of -4.9406564584124654e-324 ([-1.4821969375237396e-323, 4.9406564584124654e-324])
Matchers.tests.cpp:<line number>: passed: smallest_non_zero, !WithinULP( -smallest_non_zero, 1 ) for: 0.0 not is within 1 ULPs of -4.9406564584124654e-324 ([-9.8813129168249309e-324, -0.0000000000000000e+00])
Matchers.tests.cpp:<line number>: passed: smallest_non_zero, WithinULP( -smallest_non_zero, 2 ) for: 0.0f is within 2 ULPs of -1.40129846e-45f ([-4.20389539e-45, 1.40129846e-45])
Matchers.tests.cpp:<line number>: passed: smallest_non_zero, !WithinULP( -smallest_non_zero, 1 ) for: 0.0f not is within 1 ULPs of -1.40129846e-45f ([-2.80259693e-45, -0.00000000e+00])
Exception.tests.cpp:<line number>: failed: unexpected exception with message: 'answer := 42' with 1 message: 'expected exception'
Exception.tests.cpp:<line number>: failed: unexpected exception with message: 'answer := 42'; expression was: thisThrows() with 1 message: 'expected exception'
Exception.tests.cpp:<line number>: passed: thisThrows() with 1 message: 'answer := 42'
@ -586,6 +590,7 @@ Matchers.tests.cpp:<line number>: passed: 10.f, !WithinAbs( 11.f, 0.5f ) for: 10
Matchers.tests.cpp:<line number>: passed: -10.f, WithinAbs( -10.f, 0.5f ) for: -10.0f is within 0.5 of -10.0
Matchers.tests.cpp:<line number>: passed: -10.f, WithinAbs( -9.6f, 0.5f ) for: -10.0f is within 0.5 of -9.6000003815
Matchers.tests.cpp:<line number>: passed: 1.f, WithinULP( 1.f, 0 ) for: 1.0f is within 0 ULPs of 1.00000000e+00f ([1.00000000e+00, 1.00000000e+00])
Matchers.tests.cpp:<line number>: passed: -1.f, WithinULP( -1.f, 0 ) for: -1.0f is within 0 ULPs of -1.00000000e+00f ([-1.00000000e+00, -1.00000000e+00])
Matchers.tests.cpp:<line number>: passed: nextafter( 1.f, 2.f ), WithinULP( 1.f, 1 ) for: 1.0f is within 1 ULPs of 1.00000000e+00f ([9.99999940e-01, 1.00000012e+00])
Matchers.tests.cpp:<line number>: passed: 0.f, WithinULP( nextafter( 0.f, 1.f ), 1 ) for: 0.0f is within 1 ULPs of 1.40129846e-45f ([0.00000000e+00, 2.80259693e-45])
Matchers.tests.cpp:<line number>: passed: 1.f, WithinULP( nextafter( 1.f, 0.f ), 1 ) for: 1.0f is within 1 ULPs of 9.99999940e-01f ([9.99999881e-01, 1.00000000e+00])
@ -2065,6 +2070,16 @@ Condition.tests.cpp:<line number>: passed: long_var == unsigned_char_var for: 1
Condition.tests.cpp:<line number>: passed: long_var == unsigned_short_var for: 1 == 1
Condition.tests.cpp:<line number>: passed: long_var == unsigned_int_var for: 1 == 1
Condition.tests.cpp:<line number>: passed: long_var == unsigned_long_var for: 1 == 1
FloatingPoint.tests.cpp:<line number>: passed: convertToBits( 0.f ) == 0 for: 0 == 0
FloatingPoint.tests.cpp:<line number>: passed: convertToBits( -0.f ) == ( 1ULL << 31 ) for: 2147483648 (0x<hex digits>)
==
2147483648 (0x<hex digits>)
FloatingPoint.tests.cpp:<line number>: passed: convertToBits( 0. ) == 0 for: 0 == 0
FloatingPoint.tests.cpp:<line number>: passed: convertToBits( -0. ) == ( 1ULL << 63 ) for: 9223372036854775808 (0x<hex digits>)
==
9223372036854775808 (0x<hex digits>)
FloatingPoint.tests.cpp:<line number>: passed: convertToBits( std::numeric_limits<float>::denorm_min() ) == 1 for: 1 == 1
FloatingPoint.tests.cpp:<line number>: passed: convertToBits( std::numeric_limits<double>::denorm_min() ) == 1 for: 1 == 1
Tag.tests.cpp:<line number>: passed: Catch::TestCaseInfo("", { "test with an empty tag", "[]" }, dummySourceLineInfo)
InternalBenchmark.tests.cpp:<line number>: passed: erfc_inv(1.103560) == Approx(-0.09203687623843015) for: -0.0920368762 == Approx( -0.0920368762 )
InternalBenchmark.tests.cpp:<line number>: passed: erfc_inv(1.067400) == Approx(-0.05980291115763361) for: -0.0598029112 == Approx( -0.0598029112 )

View File

@ -1386,6 +1386,6 @@ due to unexpected exception with message:
Why would you throw a std::string?
===============================================================================
test cases: 365 | 289 passed | 70 failed | 6 failed as expected
assertions: 2092 | 1940 passed | 129 failed | 23 failed as expected
test cases: 368 | 292 passed | 70 failed | 6 failed as expected
assertions: 2103 | 1951 passed | 129 failed | 23 failed as expected

View File

@ -744,6 +744,41 @@ Misc.tests.cpp:<line number>
Misc.tests.cpp:<line number>: PASSED:
-------------------------------------------------------------------------------
#2152 - ULP checks between differently signed values were wrong - double
-------------------------------------------------------------------------------
Matchers.tests.cpp:<line number>
...............................................................................
Matchers.tests.cpp:<line number>: PASSED:
CHECK_THAT( smallest_non_zero, WithinULP( -smallest_non_zero, 2 ) )
with expansion:
0.0 is within 2 ULPs of -4.9406564584124654e-324 ([-1.4821969375237396e-323,
4.9406564584124654e-324])
Matchers.tests.cpp:<line number>: PASSED:
CHECK_THAT( smallest_non_zero, !WithinULP( -smallest_non_zero, 1 ) )
with expansion:
0.0 not is within 1 ULPs of -4.9406564584124654e-324 ([-9.8813129168249309e-
324, -0.0000000000000000e+00])
-------------------------------------------------------------------------------
#2152 - ULP checks between differently signed values were wrong - float
-------------------------------------------------------------------------------
Matchers.tests.cpp:<line number>
...............................................................................
Matchers.tests.cpp:<line number>: PASSED:
CHECK_THAT( smallest_non_zero, WithinULP( -smallest_non_zero, 2 ) )
with expansion:
0.0f is within 2 ULPs of -1.40129846e-45f ([-4.20389539e-45, 1.40129846e-45])
Matchers.tests.cpp:<line number>: PASSED:
CHECK_THAT( smallest_non_zero, !WithinULP( -smallest_non_zero, 1 ) )
with expansion:
0.0f not is within 1 ULPs of -1.40129846e-45f ([-2.80259693e-45, -0.
00000000e+00])
-------------------------------------------------------------------------------
#748 - captures with unexpected exceptions
outside assertions
@ -4490,6 +4525,12 @@ Matchers.tests.cpp:<line number>: PASSED:
with expansion:
1.0f is within 0 ULPs of 1.00000000e+00f ([1.00000000e+00, 1.00000000e+00])
Matchers.tests.cpp:<line number>: PASSED:
REQUIRE_THAT( -1.f, WithinULP( -1.f, 0 ) )
with expansion:
-1.0f is within 0 ULPs of -1.00000000e+00f ([-1.00000000e+00, -1.00000000e+
00])
Matchers.tests.cpp:<line number>: PASSED:
REQUIRE_THAT( nextafter( 1.f, 2.f ), WithinULP( 1.f, 1 ) )
with expansion:
@ -14597,6 +14638,46 @@ Condition.tests.cpp:<line number>: PASSED:
with expansion:
1 == 1
-------------------------------------------------------------------------------
convertToBits
-------------------------------------------------------------------------------
FloatingPoint.tests.cpp:<line number>
...............................................................................
FloatingPoint.tests.cpp:<line number>: PASSED:
CHECK( convertToBits( 0.f ) == 0 )
with expansion:
0 == 0
FloatingPoint.tests.cpp:<line number>: PASSED:
CHECK( convertToBits( -0.f ) == ( 1ULL << 31 ) )
with expansion:
2147483648 (0x<hex digits>)
==
2147483648 (0x<hex digits>)
FloatingPoint.tests.cpp:<line number>: PASSED:
CHECK( convertToBits( 0. ) == 0 )
with expansion:
0 == 0
FloatingPoint.tests.cpp:<line number>: PASSED:
CHECK( convertToBits( -0. ) == ( 1ULL << 63 ) )
with expansion:
9223372036854775808 (0x<hex digits>)
==
9223372036854775808 (0x<hex digits>)
FloatingPoint.tests.cpp:<line number>: PASSED:
CHECK( convertToBits( std::numeric_limits<float>::denorm_min() ) == 1 )
with expansion:
1 == 1
FloatingPoint.tests.cpp:<line number>: PASSED:
CHECK( convertToBits( std::numeric_limits<double>::denorm_min() ) == 1 )
with expansion:
1 == 1
-------------------------------------------------------------------------------
empty tags are not allowed
-------------------------------------------------------------------------------
@ -16852,6 +16933,6 @@ Misc.tests.cpp:<line number>
Misc.tests.cpp:<line number>: PASSED:
===============================================================================
test cases: 365 | 273 passed | 86 failed | 6 failed as expected
assertions: 2109 | 1940 passed | 146 failed | 23 failed as expected
test cases: 368 | 276 passed | 86 failed | 6 failed as expected
assertions: 2120 | 1951 passed | 146 failed | 23 failed as expected

View File

@ -744,6 +744,41 @@ Misc.tests.cpp:<line number>
Misc.tests.cpp:<line number>: PASSED:
-------------------------------------------------------------------------------
#2152 - ULP checks between differently signed values were wrong - double
-------------------------------------------------------------------------------
Matchers.tests.cpp:<line number>
...............................................................................
Matchers.tests.cpp:<line number>: PASSED:
CHECK_THAT( smallest_non_zero, WithinULP( -smallest_non_zero, 2 ) )
with expansion:
0.0 is within 2 ULPs of -4.9406564584124654e-324 ([-1.4821969375237396e-323,
4.9406564584124654e-324])
Matchers.tests.cpp:<line number>: PASSED:
CHECK_THAT( smallest_non_zero, !WithinULP( -smallest_non_zero, 1 ) )
with expansion:
0.0 not is within 1 ULPs of -4.9406564584124654e-324 ([-9.8813129168249309e-
324, -0.0000000000000000e+00])
-------------------------------------------------------------------------------
#2152 - ULP checks between differently signed values were wrong - float
-------------------------------------------------------------------------------
Matchers.tests.cpp:<line number>
...............................................................................
Matchers.tests.cpp:<line number>: PASSED:
CHECK_THAT( smallest_non_zero, WithinULP( -smallest_non_zero, 2 ) )
with expansion:
0.0f is within 2 ULPs of -1.40129846e-45f ([-4.20389539e-45, 1.40129846e-45])
Matchers.tests.cpp:<line number>: PASSED:
CHECK_THAT( smallest_non_zero, !WithinULP( -smallest_non_zero, 1 ) )
with expansion:
0.0f not is within 1 ULPs of -1.40129846e-45f ([-2.80259693e-45, -0.
00000000e+00])
-------------------------------------------------------------------------------
#748 - captures with unexpected exceptions
outside assertions
@ -924,6 +959,6 @@ Condition.tests.cpp:<line number>: FAILED:
CHECK( true != true )
===============================================================================
test cases: 31 | 26 passed | 3 failed | 2 failed as expected
assertions: 99 | 92 passed | 4 failed | 3 failed as expected
test cases: 33 | 28 passed | 3 failed | 2 failed as expected
assertions: 103 | 96 passed | 4 failed | 3 failed as expected

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<testsuitesloose text artifact
>
<testsuite name="<exe-name>" errors="17" failures="130" tests="2110" hostname="tbd" time="{duration}" timestamp="{iso8601-timestamp}">
<testsuite name="<exe-name>" errors="17" failures="130" tests="2121" hostname="tbd" time="{duration}" timestamp="{iso8601-timestamp}">
<properties>
<property name="filters" value="~[!nonportable]~[!benchmark]~[approvals] *"/>
<property name="random-seed" value="1"/>
@ -47,6 +47,8 @@ Nor would this
<testcase classname="<exe-name>.global" name="#1954 - 7 arg template test case sig compiles - 1, 1, 1, 1, 1, 0, 0" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="#1954 - 7 arg template test case sig compiles - 5, 1, 1, 1, 1, 0, 0" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="#1954 - 7 arg template test case sig compiles - 5, 3, 1, 1, 1, 0, 0" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="#2152 - ULP checks between differently signed values were wrong - double" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="#2152 - ULP checks between differently signed values were wrong - float" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="#748 - captures with unexpected exceptions/outside assertions" time="{duration}" status="run">
<error type="TEST_CASE">
FAILED:
@ -1548,6 +1550,7 @@ Misc.tests.cpp:<line number>
<testcase classname="<exe-name>.global" name="classify_outliers/mixed" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="comparisons between const int variables" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="comparisons between int variables" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="convertToBits" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="empty tags are not allowed" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="erfc_inv" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="estimate_clock_resolution" time="{duration}" status="run"/>

View File

@ -77,6 +77,9 @@
<testCase name="Process can be configured on command line/Benchmark options/warmup-time" duration="{duration}"/>
<testCase name="Test with special, characters &quot;in name" duration="{duration}"/>
</file>
<file path="tests/<exe-name>/IntrospectiveTests/FloatingPoint.tests.cpp">
<testCase name="convertToBits" duration="{duration}"/>
</file>
<file path="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp">
<testCase name="Generators internals/Single value" duration="{duration}"/>
<testCase name="Generators internals/Preset values" duration="{duration}"/>
@ -988,6 +991,8 @@ Exception.tests.cpp:<line number>
<testCase name="tables" duration="{duration}"/>
</file>
<file path="tests/<exe-name>/UsageTests/Matchers.tests.cpp">
<testCase name="#2152 - ULP checks between differently signed values were wrong - double" duration="{duration}"/>
<testCase name="#2152 - ULP checks between differently signed values were wrong - float" duration="{duration}"/>
<testCase name="Arbitrary predicate matcher/Function pointer" duration="{duration}"/>
<testCase name="Arbitrary predicate matcher/Lambdas + different type" duration="{duration}"/>
<testCase name="Combining MatchAllOfGeneric does not nest" duration="{duration}"/>

View File

@ -158,6 +158,14 @@ ok {test-number} -
ok {test-number} -
# #1954 - 7 arg template test case sig compiles - 5, 3, 1, 1, 1, 0, 0
ok {test-number} -
# #2152 - ULP checks between differently signed values were wrong - double
ok {test-number} - smallest_non_zero, WithinULP( -smallest_non_zero, 2 ) for: 0.0 is within 2 ULPs of -4.9406564584124654e-324 ([-1.4821969375237396e-323, 4.9406564584124654e-324])
# #2152 - ULP checks between differently signed values were wrong - double
ok {test-number} - smallest_non_zero, !WithinULP( -smallest_non_zero, 1 ) for: 0.0 not is within 1 ULPs of -4.9406564584124654e-324 ([-9.8813129168249309e-324, -0.0000000000000000e+00])
# #2152 - ULP checks between differently signed values were wrong - float
ok {test-number} - smallest_non_zero, WithinULP( -smallest_non_zero, 2 ) for: 0.0f is within 2 ULPs of -1.40129846e-45f ([-4.20389539e-45, 1.40129846e-45])
# #2152 - ULP checks between differently signed values were wrong - float
ok {test-number} - smallest_non_zero, !WithinULP( -smallest_non_zero, 1 ) for: 0.0f not is within 1 ULPs of -1.40129846e-45f ([-2.80259693e-45, -0.00000000e+00])
# #748 - captures with unexpected exceptions
not ok {test-number} - unexpected exception with message: 'answer := 42' with 1 message: 'expected exception'
# #748 - captures with unexpected exceptions
@ -1155,6 +1163,8 @@ ok {test-number} - -10.f, WithinAbs( -9.6f, 0.5f ) for: -10.0f is within 0.5 of
# Floating point matchers: float
ok {test-number} - 1.f, WithinULP( 1.f, 0 ) for: 1.0f is within 0 ULPs of 1.00000000e+00f ([1.00000000e+00, 1.00000000e+00])
# Floating point matchers: float
ok {test-number} - -1.f, WithinULP( -1.f, 0 ) for: -1.0f is within 0 ULPs of -1.00000000e+00f ([-1.00000000e+00, -1.00000000e+00])
# Floating point matchers: float
ok {test-number} - nextafter( 1.f, 2.f ), WithinULP( 1.f, 1 ) for: 1.0f is within 1 ULPs of 1.00000000e+00f ([9.99999940e-01, 1.00000012e+00])
# Floating point matchers: float
ok {test-number} - 0.f, WithinULP( nextafter( 0.f, 1.f ), 1 ) for: 0.0f is within 1 ULPs of 1.40129846e-45f ([0.00000000e+00, 2.80259693e-45])
@ -3703,6 +3713,18 @@ ok {test-number} - long_var == unsigned_short_var for: 1 == 1
ok {test-number} - long_var == unsigned_int_var for: 1 == 1
# comparisons between int variables
ok {test-number} - long_var == unsigned_long_var for: 1 == 1
# convertToBits
ok {test-number} - convertToBits( 0.f ) == 0 for: 0 == 0
# convertToBits
ok {test-number} - convertToBits( -0.f ) == ( 1ULL << 31 ) for: 2147483648 (0x<hex digits>) == 2147483648 (0x<hex digits>)
# convertToBits
ok {test-number} - convertToBits( 0. ) == 0 for: 0 == 0
# convertToBits
ok {test-number} - convertToBits( -0. ) == ( 1ULL << 63 ) for: 9223372036854775808 (0x<hex digits>) == 9223372036854775808 (0x<hex digits>)
# convertToBits
ok {test-number} - convertToBits( std::numeric_limits<float>::denorm_min() ) == 1 for: 1 == 1
# convertToBits
ok {test-number} - convertToBits( std::numeric_limits<double>::denorm_min() ) == 1 for: 1 == 1
# empty tags are not allowed
ok {test-number} - Catch::TestCaseInfo("", { "test with an empty tag", "[]" }, dummySourceLineInfo)
# erfc_inv
@ -4220,5 +4242,5 @@ ok {test-number} - q3 == 23. for: 23.0 == 23.0
ok {test-number} -
# xmlentitycheck
ok {test-number} -
1..2109
1..2120

View File

@ -50,6 +50,10 @@ Tricky.tests.cpp:<line number>|nexplicit failure with message:|n "1514"']
##teamcity[testFinished name='#1954 - 7 arg template test case sig compiles - 5, 1, 1, 1, 1, 0, 0' duration="{duration}"]
##teamcity[testStarted name='#1954 - 7 arg template test case sig compiles - 5, 3, 1, 1, 1, 0, 0']
##teamcity[testFinished name='#1954 - 7 arg template test case sig compiles - 5, 3, 1, 1, 1, 0, 0' duration="{duration}"]
##teamcity[testStarted name='#2152 - ULP checks between differently signed values were wrong - double']
##teamcity[testFinished name='#2152 - ULP checks between differently signed values were wrong - double' duration="{duration}"]
##teamcity[testStarted name='#2152 - ULP checks between differently signed values were wrong - float']
##teamcity[testFinished name='#2152 - ULP checks between differently signed values were wrong - float' duration="{duration}"]
##teamcity[testStarted name='#748 - captures with unexpected exceptions']
Exception.tests.cpp:<line number>|nunexpected exception with messages:|n "answer := 42"|n "expected exception"- failure ignore as test marked as |'ok to fail|'|n']
Exception.tests.cpp:<line number>|nunexpected exception with messages:|n "answer := 42"|n "expected exception"|n REQUIRE_NOTHROW( thisThrows() )|nwith expansion:|n thisThrows()|n- failure ignore as test marked as |'ok to fail|'|n']
@ -690,6 +694,8 @@ Misc.tests.cpp:<line number>|nexpression failed|n REQUIRE( testCheckedIf( false
##teamcity[testFinished name='comparisons between const int variables' duration="{duration}"]
##teamcity[testStarted name='comparisons between int variables']
##teamcity[testFinished name='comparisons between int variables' duration="{duration}"]
##teamcity[testStarted name='convertToBits']
##teamcity[testFinished name='convertToBits' duration="{duration}"]
##teamcity[testStarted name='empty tags are not allowed']
##teamcity[testFinished name='empty tags are not allowed' duration="{duration}"]
##teamcity[testStarted name='erfc_inv']

View File

@ -650,6 +650,44 @@ Nor would this
<TestCase name="#1954 - 7 arg template test case sig compiles - 5, 3, 1, 1, 1, 0, 0" tags="[.][compilation][regression]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
<OverallResult success="true"/>
</TestCase>
<TestCase name="#2152 - ULP checks between differently signed values were wrong - double" tags="[floating-point][matchers][ulp]" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
<Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
<Original>
smallest_non_zero, WithinULP( -smallest_non_zero, 2 )
</Original>
<Expanded>
0.0 is within 2 ULPs of -4.9406564584124654e-324 ([-1.4821969375237396e-323, 4.9406564584124654e-324])
</Expanded>
</Expression>
<Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
<Original>
smallest_non_zero, !WithinULP( -smallest_non_zero, 1 )
</Original>
<Expanded>
0.0 not is within 1 ULPs of -4.9406564584124654e-324 ([-9.8813129168249309e-324, -0.0000000000000000e+00])
</Expanded>
</Expression>
<OverallResult success="true"/>
</TestCase>
<TestCase name="#2152 - ULP checks between differently signed values were wrong - float" tags="[floating-point][matchers][ulp]" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
<Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
<Original>
smallest_non_zero, WithinULP( -smallest_non_zero, 2 )
</Original>
<Expanded>
0.0f is within 2 ULPs of -1.40129846e-45f ([-4.20389539e-45, 1.40129846e-45])
</Expanded>
</Expression>
<Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
<Original>
smallest_non_zero, !WithinULP( -smallest_non_zero, 1 )
</Original>
<Expanded>
0.0f not is within 1 ULPs of -1.40129846e-45f ([-2.80259693e-45, -0.00000000e+00])
</Expanded>
</Expression>
<OverallResult success="true"/>
</TestCase>
<TestCase name="#748 - captures with unexpected exceptions" tags="[!shouldfail][!throws][.][failing]" filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
<Section name="outside assertions" filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
<Info>
@ -5087,6 +5125,14 @@ Nor would this
1.0f is within 0 ULPs of 1.00000000e+00f ([1.00000000e+00, 1.00000000e+00])
</Expanded>
</Expression>
<Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
<Original>
-1.f, WithinULP( -1.f, 0 )
</Original>
<Expanded>
-1.0f is within 0 ULPs of -1.00000000e+00f ([-1.00000000e+00, -1.00000000e+00])
</Expanded>
</Expression>
<Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
<Original>
nextafter( 1.f, 2.f ), WithinULP( 1.f, 1 )
@ -5135,7 +5181,7 @@ Nor would this
-0.0f is within 0 ULPs of 0.00000000e+00f ([0.00000000e+00, 0.00000000e+00])
</Expanded>
</Expression>
<OverallResults successes="7" failures="0" expectedFailures="0"/>
<OverallResults successes="8" failures="0" expectedFailures="0"/>
</Section>
<Section name="Composed" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
<Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
@ -17257,6 +17303,61 @@ There is no extra whitespace here
</Expression>
<OverallResult success="true"/>
</TestCase>
<TestCase name="convertToBits" tags="[conversion][floating-point]" filename="tests/<exe-name>/IntrospectiveTests/FloatingPoint.tests.cpp" >
<Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/FloatingPoint.tests.cpp" >
<Original>
convertToBits( 0.f ) == 0
</Original>
<Expanded>
0 == 0
</Expanded>
</Expression>
<Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/FloatingPoint.tests.cpp" >
<Original>
convertToBits( -0.f ) == ( 1ULL &lt;&lt; 31 )
</Original>
<Expanded>
2147483648 (0x<hex digits>)
==
2147483648 (0x<hex digits>)
</Expanded>
</Expression>
<Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/FloatingPoint.tests.cpp" >
<Original>
convertToBits( 0. ) == 0
</Original>
<Expanded>
0 == 0
</Expanded>
</Expression>
<Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/FloatingPoint.tests.cpp" >
<Original>
convertToBits( -0. ) == ( 1ULL &lt;&lt; 63 )
</Original>
<Expanded>
9223372036854775808 (0x<hex digits>)
==
9223372036854775808 (0x<hex digits>)
</Expanded>
</Expression>
<Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/FloatingPoint.tests.cpp" >
<Original>
convertToBits( std::numeric_limits&lt;float>::denorm_min() ) == 1
</Original>
<Expanded>
1 == 1
</Expanded>
</Expression>
<Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/FloatingPoint.tests.cpp" >
<Original>
convertToBits( std::numeric_limits&lt;double>::denorm_min() ) == 1
</Original>
<Expanded>
1 == 1
</Expanded>
</Expression>
<OverallResult success="true"/>
</TestCase>
<TestCase name="empty tags are not allowed" tags="[tags]" filename="tests/<exe-name>/IntrospectiveTests/Tag.tests.cpp" >
<Expression success="true" type="REQUIRE_THROWS" filename="tests/<exe-name>/IntrospectiveTests/Tag.tests.cpp" >
<Original>
@ -19826,9 +19927,9 @@ loose text artifact
</Section>
<OverallResult success="true"/>
</TestCase>
<OverallResults successes="1940" failures="147" expectedFailures="23"/>
<OverallResultsCases successes="273" failures="86" expectedFailures="6"/>
<OverallResults successes="1951" failures="147" expectedFailures="23"/>
<OverallResultsCases successes="276" failures="86" expectedFailures="6"/>
</Group>
<OverallResults successes="1940" failures="146" expectedFailures="23"/>
<OverallResultsCases successes="273" failures="86" expectedFailures="6"/>
<OverallResults successes="1951" failures="146" expectedFailures="23"/>
<OverallResultsCases successes="276" failures="86" expectedFailures="6"/>
</Catch>

View File

@ -0,0 +1,66 @@
#include <catch2/catch_test_macros.hpp>
#include <catch2/catch_template_test_macros.hpp>
#include <catch2/internal/catch_floating_point_helpers.hpp>
TEST_CASE("convertToBits", "[floating-point][conversion]") {
using Catch::Detail::convertToBits;
CHECK( convertToBits( 0.f ) == 0 );
CHECK( convertToBits( -0.f ) == ( 1ULL << 31 ) );
CHECK( convertToBits( 0. ) == 0 );
CHECK( convertToBits( -0. ) == ( 1ULL << 63 ) );
CHECK( convertToBits( std::numeric_limits<float>::denorm_min() ) == 1 );
CHECK( convertToBits( std::numeric_limits<double>::denorm_min() ) == 1 );
}
TEMPLATE_TEST_CASE("type-shared ulpDistance tests", "[floating-point][ulp][approvals]", float, double) {
using FP = TestType;
using Catch::ulpDistance;
// Distance between zeros is zero
CHECK( ulpDistance( FP{}, FP{} ) == 0 );
CHECK( ulpDistance( FP{}, -FP{} ) == 0 );
CHECK( ulpDistance( -FP{}, -FP{} ) == 0 );
// Distance between same-sign infinities is zero
static constexpr FP infinity = std::numeric_limits<FP>::infinity();
CHECK( ulpDistance( infinity, infinity ) == 0 );
CHECK( ulpDistance( -infinity, -infinity ) == 0 );
// Distance between max-finite-val and same sign infinity is 1
static constexpr FP max_finite = std::numeric_limits<FP>::max();
CHECK( ulpDistance( max_finite, infinity ) == 1 );
CHECK( ulpDistance( -max_finite, -infinity ) == 1 );
// Distance between X and 0 is half of distance between X and -X
CHECK( ulpDistance( -infinity, infinity ) ==
2 * ulpDistance( infinity, FP{} ) );
CHECK( 2 * ulpDistance( FP{ -2. }, FP{} ) ==
ulpDistance( FP{ -2. }, FP{ 2. } ) );
CHECK( 2 * ulpDistance( FP{ 2. }, FP{} ) ==
ulpDistance( FP{ -2. }, FP{ 2. } ) );
// Denorms are supported
CHECK( ulpDistance( std::numeric_limits<FP>::denorm_min(), FP{} ) == 1 );
CHECK( ulpDistance( std::numeric_limits<FP>::denorm_min(), -FP{} ) == 1 );
CHECK( ulpDistance( -std::numeric_limits<FP>::denorm_min(), FP{} ) == 1 );
CHECK( ulpDistance( -std::numeric_limits<FP>::denorm_min(), -FP{} ) == 1 );
CHECK( ulpDistance( std::numeric_limits<FP>::denorm_min(),
-std::numeric_limits<FP>::denorm_min() ) == 2 );
// Machine epsilon
CHECK( ulpDistance( FP{ 1. },
FP{ 1. } + std::numeric_limits<FP>::epsilon() ) == 1 );
CHECK( ulpDistance( -FP{ 1. },
-FP{ 1. } - std::numeric_limits<FP>::epsilon() ) == 1 );
}
TEST_CASE("UlpDistance", "[floating-point][ulp][approvals]") {
using Catch::ulpDistance;
CHECK( ulpDistance( 1., 2. ) == 0x10'00'00'00'00'00'00 );
CHECK( ulpDistance( -2., 2. ) == 0x80'00'00'00'00'00'00'00 );
CHECK( ulpDistance( 1.f, 2.f ) == 0x80'00'00 );
CHECK( ulpDistance( -2.f, 2.f ) == 0x80'00'00'00 );
}

View File

@ -4,6 +4,7 @@
*/
#include <catch2/catch_test_macros.hpp>
#include <catch2/catch_template_test_macros.hpp>
#include <catch2/matchers/catch_matchers_exception.hpp>
#include <catch2/matchers/catch_matchers_floating_point.hpp>
#include <catch2/matchers/catch_matchers_predicate.hpp>
@ -465,6 +466,7 @@ TEST_CASE( "Floating point matchers: float", "[matchers][floating-point]" ) {
}
SECTION( "ULPs" ) {
REQUIRE_THAT( 1.f, WithinULP( 1.f, 0 ) );
REQUIRE_THAT(-1.f, WithinULP( -1.f, 0 ) );
REQUIRE_THAT( nextafter( 1.f, 2.f ), WithinULP( 1.f, 1 ) );
REQUIRE_THAT( 0.f, WithinULP( nextafter( 0.f, 1.f ), 1 ) );
@ -1081,3 +1083,17 @@ TEST_CASE( "Matchers can take references",
#ifdef __clang__
# pragma clang diagnostic pop
#endif
TEMPLATE_TEST_CASE(
"#2152 - ULP checks between differently signed values were wrong",
"[matchers][floating-point][ulp]",
float,
double ) {
using Catch::Matchers::WithinULP;
static constexpr TestType smallest_non_zero =
std::numeric_limits<TestType>::denorm_min();
CHECK_THAT( smallest_non_zero, WithinULP( -smallest_non_zero, 2 ) );
CHECK_THAT( smallest_non_zero, !WithinULP( -smallest_non_zero, 1 ) );
}