Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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
- Loading branch information
Showing
18 changed files
with
479 additions
and
38 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
#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 | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
|
||
// 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 | ||
|
||
|
||
// tbd: | ||
// * x == y <--> ulpDistance(x, y) == 0 | ||
// * x == -y <--> 2* ulpDistance(x, 0) | ||
// * ulpDistance(inf, hugeVal) == 1 | ||
// * doesn't behave quite like nextafter/nexttowards | ||
// * Assumes IEEE-754 representation | ||
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 ( ( lhs < 0 ) != ( rhs < 0 ) ) { | ||
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.