tint: Have Number equality consider sign

Sign is important for equality when it comes to backend generation
of floating point numbers.

Change-Id: I1e2610fe9bae98a5c5f756a55385e092919b5aa3
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/95080
Commit-Queue: Ben Clayton <bclayton@chromium.org>
Reviewed-by: Dan Sinclair <dsinclair@chromium.org>
Kokoro: Kokoro <noreply+kokoro@google.com>
This commit is contained in:
Ben Clayton 2022-06-29 17:09:11 +00:00 committed by Dawn LUCI CQ
parent 4287a53bf7
commit cf52af7b0f
2 changed files with 139 additions and 56 deletions

View File

@ -16,6 +16,7 @@
#define SRC_TINT_NUMBER_H_
#include <stdint.h>
#include <cmath>
#include <functional>
#include <limits>
#include <optional>
@ -135,61 +136,6 @@ inline std::ostream& operator<<(std::ostream& out, Number<T> num) {
return out << num.value;
}
/// Equality operator.
/// @param a the LHS number
/// @param b the RHS number
/// @returns true if the numbers `a` and `b` are exactly equal.
template <typename A, typename B>
bool operator==(Number<A> a, Number<B> b) {
using T = decltype(a.value + b.value);
return std::equal_to<T>()(static_cast<T>(a.value), static_cast<T>(b.value));
}
/// Inequality operator.
/// @param a the LHS number
/// @param b the RHS number
/// @returns true if the numbers `a` and `b` are exactly unequal.
template <typename A, typename B>
bool operator!=(Number<A> a, Number<B> b) {
return !(a == b);
}
/// Equality operator.
/// @param a the LHS number
/// @param b the RHS number
/// @returns true if the numbers `a` and `b` are exactly equal.
template <typename A, typename B>
std::enable_if_t<IsNumeric<B>, bool> operator==(Number<A> a, B b) {
return a == Number<B>(b);
}
/// Inequality operator.
/// @param a the LHS number
/// @param b the RHS number
/// @returns true if the numbers `a` and `b` are exactly unequal.
template <typename A, typename B>
std::enable_if_t<IsNumeric<B>, bool> operator!=(Number<A> a, B b) {
return !(a == b);
}
/// Equality operator.
/// @param a the LHS number
/// @param b the RHS number
/// @returns true if the numbers `a` and `b` are exactly equal.
template <typename A, typename B>
std::enable_if_t<IsNumeric<A>, bool> operator==(A a, Number<B> b) {
return Number<A>(a) == b;
}
/// Inequality operator.
/// @param a the LHS number
/// @param b the RHS number
/// @returns true if the numbers `a` and `b` are exactly unequal.
template <typename A, typename B>
std::enable_if_t<IsNumeric<A>, bool> operator!=(A a, Number<B> b) {
return !(a == b);
}
/// The partial specification of Number for f16 type, storing the f16 value as float,
/// and enforcing proper explicit casting.
template <>
@ -295,6 +241,70 @@ utils::Result<TO, ConversionFailure> CheckedConvert(Number<FROM> num) {
return TO(value); // Success
}
/// Equality operator.
/// @param a the LHS number
/// @param b the RHS number
/// @returns true if the numbers `a` and `b` are exactly equal. Also considers sign bit.
template <typename A, typename B>
bool operator==(Number<A> a, Number<B> b) {
// Use the highest-precision integer or floating-point type to perform the comparisons.
using T =
std::conditional_t<IsFloatingPoint<A> || IsFloatingPoint<B>, AFloat::type, AInt::type>;
auto va = static_cast<T>(a.value);
auto vb = static_cast<T>(b.value);
if constexpr (IsFloatingPoint<T>) {
if (std::signbit(va) != std::signbit(vb)) {
return false;
}
}
return std::equal_to<T>()(va, vb);
}
/// Inequality operator.
/// @param a the LHS number
/// @param b the RHS number
/// @returns true if the numbers `a` and `b` are exactly unequal. Also considers sign bit.
template <typename A, typename B>
bool operator!=(Number<A> a, Number<B> b) {
return !(a == b);
}
/// Equality operator.
/// @param a the LHS number
/// @param b the RHS number
/// @returns true if the numbers `a` and `b` are exactly equal.
template <typename A, typename B>
std::enable_if_t<IsNumeric<B>, bool> operator==(Number<A> a, B b) {
return a == Number<B>(b);
}
/// Inequality operator.
/// @param a the LHS number
/// @param b the RHS number
/// @returns true if the numbers `a` and `b` are exactly unequal.
template <typename A, typename B>
std::enable_if_t<IsNumeric<B>, bool> operator!=(Number<A> a, B b) {
return !(a == b);
}
/// Equality operator.
/// @param a the LHS number
/// @param b the RHS number
/// @returns true if the numbers `a` and `b` are exactly equal.
template <typename A, typename B>
std::enable_if_t<IsNumeric<A>, bool> operator==(A a, Number<B> b) {
return Number<A>(a) == b;
}
/// Inequality operator.
/// @param a the LHS number
/// @param b the RHS number
/// @returns true if the numbers `a` and `b` are exactly unequal.
template <typename A, typename B>
std::enable_if_t<IsNumeric<A>, bool> operator!=(A a, Number<B> b) {
return !(a == b);
}
/// Define 'TINT_HAS_OVERFLOW_BUILTINS' if the compiler provide overflow checking builtins.
/// If the compiler does not support these builtins, then these are emulated with algorithms
/// described in:

View File

@ -65,6 +65,79 @@ constexpr double kLowestF16NextULP = -kHighestF16NextULP;
// warning.
TINT_BEGIN_DISABLE_WARNING(CONSTANT_OVERFLOW);
TEST(NumberTest, Equality) {
EXPECT_TRUE(0_a == 0_a);
EXPECT_TRUE(10_a == 10_a);
EXPECT_TRUE(-10_a == -10_a);
EXPECT_TRUE(0_i == 0_i);
EXPECT_TRUE(10_i == 10_i);
EXPECT_TRUE(-10_i == -10_i);
EXPECT_TRUE(0_u == 0_u);
EXPECT_TRUE(10_u == 10_u);
EXPECT_TRUE(0._a == 0._a);
EXPECT_TRUE(-0._a == -0._a);
EXPECT_TRUE(10._a == 10._a);
EXPECT_TRUE(-10._a == -10._a);
EXPECT_TRUE(0_f == 0_f);
EXPECT_TRUE(-0_f == -0_f);
EXPECT_TRUE(10_f == 10_f);
EXPECT_TRUE(-10_f == -10_f);
EXPECT_TRUE(0_h == 0_h);
EXPECT_TRUE(-0_h == -0_h);
EXPECT_TRUE(10_h == 10_h);
EXPECT_TRUE(-10_h == -10_h);
}
TEST(NumberTest, Inequality) {
EXPECT_TRUE(0_a != 1_a);
EXPECT_TRUE(10_a != 11_a);
EXPECT_TRUE(11_a != 10_a);
EXPECT_TRUE(-10_a != -11_a);
EXPECT_TRUE(-11_a != -10_a);
EXPECT_TRUE(0_i != 1_i);
EXPECT_TRUE(1_i != 0_i);
EXPECT_TRUE(10_i != 11_i);
EXPECT_TRUE(11_i != 10_i);
EXPECT_TRUE(-10_i != -11_i);
EXPECT_TRUE(-11_i != -10_i);
EXPECT_TRUE(0_u != 1_u);
EXPECT_TRUE(1_u != 0_u);
EXPECT_TRUE(10_u != 11_u);
EXPECT_TRUE(11_u != 10_u);
EXPECT_TRUE(0._a != -0._a);
EXPECT_TRUE(-0._a != 0._a);
EXPECT_TRUE(10._a != 11._a);
EXPECT_TRUE(11._a != 10._a);
EXPECT_TRUE(-10._a != -11._a);
EXPECT_TRUE(-11._a != -10._a);
EXPECT_TRUE(0_f != -0_f);
EXPECT_TRUE(-0_f != 0_f);
EXPECT_TRUE(-0_f != -1_f);
EXPECT_TRUE(-1_f != -0_f);
EXPECT_TRUE(10_f != -10_f);
EXPECT_TRUE(-10_f != 10_f);
EXPECT_TRUE(10_f != 11_f);
EXPECT_TRUE(-10_f != -11_f);
EXPECT_TRUE(0_h != -0_h);
EXPECT_TRUE(-0_h != 0_h);
EXPECT_TRUE(-0_h != -1_h);
EXPECT_TRUE(-1_h != -0_h);
EXPECT_TRUE(10_h != -10_h);
EXPECT_TRUE(-10_h != 10_h);
EXPECT_TRUE(10_h != 11_h);
EXPECT_TRUE(-10_h != -11_h);
}
TEST(NumberTest, CheckedConvertIdentity) {
EXPECT_EQ(CheckedConvert<AInt>(0_a), 0_a);
EXPECT_EQ(CheckedConvert<AFloat>(0_a), 0.0_a);
@ -211,7 +284,7 @@ TEST(NumberTest, QuantizeF16) {
EXPECT_EQ(f16(lowestNegativeSubnormalF16PlusULP), -0x0.ff8p-14);
EXPECT_EQ(f16(highestNegativeSubnormalF16MinusULP), highestNegativeSubnormalF16);
EXPECT_EQ(f16(highestNegativeSubnormalF16), highestNegativeSubnormalF16);
EXPECT_EQ(f16(highestNegativeSubnormalF16PlusULP), 0.0);
EXPECT_EQ(f16(highestNegativeSubnormalF16PlusULP), -0.0);
// Test the mantissa discarding.
EXPECT_EQ(f16(-0x0.064p-14), -0x0.064p-14);
EXPECT_EQ(f16(-0x0.067fecp-14), -0x0.064p-14);