Tests: Compare infinities and zero signs exactly in ULP checks ulp_distance folded -0 onto +0 and put an infinity one step from the largest finite value, so every ULP budget accepted +0 for sqrt(-0), and any budget of one or more accepted overflow. An infinity is now as far as possible from any other value, and so is a zero from the zero of the other sign; the pmadd check inherits the exact zero sign. Validation: C++14, Release, seed 1. GPU parts 1, 3 and 7 pass with GCC 13.3 / NVCC 12.8 and with Clang 20 / CUDA 12.8 (-ffp-contract=fast) on an NVIDIA GeForce RTX 4070 Laptop GPU (sm_89) under WSL2. GCC 13.3 passes all 15 packetmath parts, the four special_packetmath parts and packetmath_fastmath. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
diff --git a/test/packetmath_gpu.cu b/test/packetmath_gpu.cu index 277465f..b4691c6 100644 --- a/test/packetmath_gpu.cu +++ b/test/packetmath_gpu.cu
@@ -533,6 +533,12 @@ in, [](Scalar x) { return std::round(x); }, bits); check_unary<Packet, op_preinterpret_self>( in, [](Scalar x) { return x; }, bits_and_payload); + // No ULP budget admits a lost zero sign or an overflow. + const uint64_t kFar = (std::numeric_limits<uint64_t>::max)(); + VERIFY_IS_EQUAL(test::ulp_distance(Scalar(0), Scalar(-0.0)), kFar); + VERIFY_IS_EQUAL(test::ulp_distance(std::numeric_limits<Scalar>::infinity(), (std::numeric_limits<Scalar>::max)()), + kFar); + VERIFY_IS_EQUAL(test::ulp_distance(Scalar(0), std::numeric_limits<Scalar>::denorm_min()), uint64_t(1)); if (std::is_same<Scalar, float>::value) { check_unary<Packet, op_psqrt>( in, [](Scalar x) { return std::sqrt(x); }, compare_ulps<Scalar>{kSqrtFloatUlps});
diff --git a/test/packetmath_test_shared.h b/test/packetmath_test_shared.h index 5a3bd8e..db5db6d 100644 --- a/test/packetmath_test_shared.h +++ b/test/packetmath_test_shared.h
@@ -268,13 +268,19 @@ } // Distance in units in the last place between two values of the same floating-point type. Two NaNs are zero apart, -// a NaN and a number as far apart as possible. (test/ulp_accuracy measures signed errors with its own fold, which -// maps -0 below +0 and treats infinities as incomparable; the budgets it reports are not this distance.) +// a NaN and a number as far apart as possible. Infinities and the sign of zero are exact: an infinity is as far as +// possible from any other value, and +0 from -0, so no budget admits an overflow or a lost sign. (test/ulp_accuracy +// measures signed errors with its own fold, which maps -0 below +0 and treats infinities as incomparable; the budgets +// it reports are not this distance.) template <typename Scalar> uint64_t ulp_distance(Scalar a, Scalar b) { + constexpr uint64_t kFar = (std::numeric_limits<uint64_t>::max)(); const bool a_nan = (numext::isnan)(a); const bool b_nan = (numext::isnan)(b); - if (a_nan || b_nan) return (a_nan && b_nan) ? 0 : (std::numeric_limits<uint64_t>::max)(); + if (a_nan || b_nan) return (a_nan && b_nan) ? 0 : kFar; + if ((numext::isinf)(a) || (numext::isinf)(b) || (numext::is_exactly_zero(a) && numext::is_exactly_zero(b))) { + return biteq(a, b) ? 0 : kFar; + } const int64_t pa = static_cast<int64_t>(ordered_position(a)); const int64_t pb = static_cast<int64_t>(ordered_position(b)); return pa > pb ? uint64_t(pa) - uint64_t(pb) : uint64_t(pb) - uint64_t(pa);