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>
diff --git a/src/tint/number.h b/src/tint/number.h
index 4e3e6fd..e130019 100644
--- a/src/tint/number.h
+++ b/src/tint/number.h
@@ -16,6 +16,7 @@
#define SRC_TINT_NUMBER_H_
#include <stdint.h>
+#include <cmath>
#include <functional>
#include <limits>
#include <optional>
@@ -135,61 +136,6 @@
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 @@
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:
diff --git a/src/tint/number_test.cc b/src/tint/number_test.cc
index 6a48f9d..81acc04 100644
--- a/src/tint/number_test.cc
+++ b/src/tint/number_test.cc
@@ -65,6 +65,79 @@
// 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 @@
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);