From 79ecb814d0c929a66ad92c7b3e91191f01247ac1 Mon Sep 17 00:00:00 2001 From: lntue Date: Tue, 1 Oct 2024 06:37:47 -0400 Subject: [PATCH] [libc][math] Fix exceptional cases pow(-0, 1/2) and pow(-inf, 1/2). (#110566) --- libc/src/math/generic/pow.cpp | 9 ++++++++- libc/src/math/generic/powf.cpp | 5 +++++ libc/test/src/math/smoke/pow_test.cpp | 5 +++++ libc/test/src/math/smoke/powf_test.cpp | 7 ++++++- 4 files changed, 24 insertions(+), 2 deletions(-) diff --git a/libc/src/math/generic/pow.cpp b/libc/src/math/generic/pow.cpp index 20f914430261c1..3a50e220154e51 100644 --- a/libc/src/math/generic/pow.cpp +++ b/libc/src/math/generic/pow.cpp @@ -231,9 +231,16 @@ LLVM_LIBC_FUNCTION(double, pow, (double x, double y)) { switch (y_a) { case 0: // y = +-0.0 return 1.0; - case 0x3fe0'0000'0000'0000: // y = +-0.5 + case 0x3fe0'0000'0000'0000: { // y = +-0.5 // TODO: speed up x^(-1/2) with rsqrt(x) when available. + if (LIBC_UNLIKELY(!y_sign && (x_u == FPBits::zero(Sign::NEG).uintval() || + x_u == FPBits::inf(Sign::NEG).uintval()))) { + // pow(-0, 1/2) = +0 + // pow(-inf, 1/2) = +inf + return FPBits(x_abs).get_val(); + } return y_sign ? (1.0 / fputil::sqrt(x)) : fputil::sqrt(x); + } case 0x3ff0'0000'0000'0000: // y = +-1.0 return y_sign ? (1.0 / x) : x; case 0x4000'0000'0000'0000: // y = +-2.0; diff --git a/libc/src/math/generic/powf.cpp b/libc/src/math/generic/powf.cpp index 845ac2521e0905..e3dee678a1a70a 100644 --- a/libc/src/math/generic/powf.cpp +++ b/libc/src/math/generic/powf.cpp @@ -563,6 +563,11 @@ LLVM_LIBC_FUNCTION(float, powf, (float x, float y)) { switch (y_u) { case 0x3f00'0000: // y = 0.5f // pow(x, 1/2) = sqrt(x) + if (LIBC_UNLIKELY(x_u == 0x8000'0000 || x_u == 0xff80'0000)) { + // pow(-0, 1/2) = +0 + // pow(-inf, 1/2) = +inf + return FloatBits(x_abs).get_val(); + } return fputil::sqrt(x); case 0x3f80'0000: // y = 1.0f return x; diff --git a/libc/test/src/math/smoke/pow_test.cpp b/libc/test/src/math/smoke/pow_test.cpp index 4f2a3a28c0dcbe..7f0136d783c6ba 100644 --- a/libc/test/src/math/smoke/pow_test.cpp +++ b/libc/test/src/math/smoke/pow_test.cpp @@ -22,6 +22,7 @@ TEST_F(LlvmLibcPowTest, SpecialNumbers) { constexpr double POS_ODD_INTEGER = 5.0; constexpr double POS_EVEN_INTEGER = 8.0; constexpr double POS_NON_INTEGER = 1.1; + constexpr double ONE_HALF = 0.5; for (int i = 0; i < N_ROUNDING_MODES; ++i) { ForceRoundingMode __r(ROUNDING_MODES[i]); @@ -38,6 +39,7 @@ TEST_F(LlvmLibcPowTest, SpecialNumbers) { EXPECT_FP_EQ(zero, LIBC_NAMESPACE::pow(zero, POS_ODD_INTEGER)); EXPECT_FP_EQ(zero, LIBC_NAMESPACE::pow(zero, POS_EVEN_INTEGER)); EXPECT_FP_EQ(zero, LIBC_NAMESPACE::pow(zero, POS_NON_INTEGER)); + EXPECT_FP_EQ(zero, LIBC_NAMESPACE::pow(zero, ONE_HALF)); EXPECT_FP_EQ(1.0, LIBC_NAMESPACE::pow(zero, zero)); EXPECT_FP_EQ(1.0, LIBC_NAMESPACE::pow(zero, neg_zero)); EXPECT_FP_EQ(0.0, LIBC_NAMESPACE::pow(zero, inf)); @@ -55,6 +57,7 @@ TEST_F(LlvmLibcPowTest, SpecialNumbers) { EXPECT_FP_EQ(neg_zero, LIBC_NAMESPACE::pow(neg_zero, POS_ODD_INTEGER)); EXPECT_FP_EQ(zero, LIBC_NAMESPACE::pow(neg_zero, POS_EVEN_INTEGER)); EXPECT_FP_EQ(zero, LIBC_NAMESPACE::pow(neg_zero, POS_NON_INTEGER)); + EXPECT_FP_EQ(zero, LIBC_NAMESPACE::pow(neg_zero, ONE_HALF)); EXPECT_FP_EQ(1.0, LIBC_NAMESPACE::pow(neg_zero, zero)); EXPECT_FP_EQ(1.0, LIBC_NAMESPACE::pow(neg_zero, neg_zero)); EXPECT_FP_EQ(0.0, LIBC_NAMESPACE::pow(neg_zero, inf)); @@ -105,6 +108,7 @@ TEST_F(LlvmLibcPowTest, SpecialNumbers) { EXPECT_FP_EQ(inf, LIBC_NAMESPACE::pow(inf, POS_ODD_INTEGER)); EXPECT_FP_EQ(inf, LIBC_NAMESPACE::pow(inf, POS_EVEN_INTEGER)); EXPECT_FP_EQ(inf, LIBC_NAMESPACE::pow(inf, POS_NON_INTEGER)); + EXPECT_FP_EQ(inf, LIBC_NAMESPACE::pow(inf, ONE_HALF)); EXPECT_FP_EQ(inf, LIBC_NAMESPACE::pow(inf, inf)); EXPECT_FP_EQ(zero, LIBC_NAMESPACE::pow(inf, neg_inf)); EXPECT_FP_IS_NAN(LIBC_NAMESPACE::pow(inf, aNaN)); @@ -120,6 +124,7 @@ TEST_F(LlvmLibcPowTest, SpecialNumbers) { EXPECT_FP_EQ(neg_inf, LIBC_NAMESPACE::pow(neg_inf, POS_ODD_INTEGER)); EXPECT_FP_EQ(inf, LIBC_NAMESPACE::pow(neg_inf, POS_EVEN_INTEGER)); EXPECT_FP_EQ(inf, LIBC_NAMESPACE::pow(neg_inf, POS_NON_INTEGER)); + EXPECT_FP_EQ(inf, LIBC_NAMESPACE::pow(neg_inf, ONE_HALF)); EXPECT_FP_EQ(inf, LIBC_NAMESPACE::pow(neg_inf, inf)); EXPECT_FP_EQ(zero, LIBC_NAMESPACE::pow(neg_inf, neg_inf)); EXPECT_FP_IS_NAN(LIBC_NAMESPACE::pow(neg_inf, aNaN)); diff --git a/libc/test/src/math/smoke/powf_test.cpp b/libc/test/src/math/smoke/powf_test.cpp index 98a532f3468c73..6aa1ac2b661be2 100644 --- a/libc/test/src/math/smoke/powf_test.cpp +++ b/libc/test/src/math/smoke/powf_test.cpp @@ -25,7 +25,8 @@ TEST_F(LlvmLibcPowfTest, SpecialNumbers) { constexpr float neg_non_integer = -1.1f; constexpr float pos_odd_integer = 5.0f; constexpr float pos_even_integer = 8.0f; - constexpr float pos_non_integer = 1.1f; + constexpr float pos_non_integer = 1.3f; + constexpr float one_half = 0.5f; for (int i = 0; i < N_ROUNDING_MODES; ++i) { ForceRoundingMode __r(ROUNDING_MODES[i]); @@ -42,6 +43,7 @@ TEST_F(LlvmLibcPowfTest, SpecialNumbers) { EXPECT_FP_EQ(zero, LIBC_NAMESPACE::powf(zero, pos_odd_integer)); EXPECT_FP_EQ(zero, LIBC_NAMESPACE::powf(zero, pos_even_integer)); EXPECT_FP_EQ(zero, LIBC_NAMESPACE::powf(zero, pos_non_integer)); + EXPECT_FP_EQ(zero, LIBC_NAMESPACE::powf(zero, one_half)); EXPECT_FP_EQ(1.0f, LIBC_NAMESPACE::powf(zero, zero)); EXPECT_FP_EQ(1.0f, LIBC_NAMESPACE::powf(zero, neg_zero)); EXPECT_FP_EQ(0.0f, LIBC_NAMESPACE::powf(zero, inf)); @@ -59,6 +61,7 @@ TEST_F(LlvmLibcPowfTest, SpecialNumbers) { EXPECT_FP_EQ(neg_zero, LIBC_NAMESPACE::powf(neg_zero, pos_odd_integer)); EXPECT_FP_EQ(zero, LIBC_NAMESPACE::powf(neg_zero, pos_even_integer)); EXPECT_FP_EQ(zero, LIBC_NAMESPACE::powf(neg_zero, pos_non_integer)); + EXPECT_FP_EQ(zero, LIBC_NAMESPACE::powf(neg_zero, one_half)); EXPECT_FP_EQ(1.0f, LIBC_NAMESPACE::powf(neg_zero, zero)); EXPECT_FP_EQ(1.0f, LIBC_NAMESPACE::powf(neg_zero, neg_zero)); EXPECT_FP_EQ(0.0f, LIBC_NAMESPACE::powf(neg_zero, inf)); @@ -109,6 +112,7 @@ TEST_F(LlvmLibcPowfTest, SpecialNumbers) { EXPECT_FP_EQ(inf, LIBC_NAMESPACE::powf(inf, pos_odd_integer)); EXPECT_FP_EQ(inf, LIBC_NAMESPACE::powf(inf, pos_even_integer)); EXPECT_FP_EQ(inf, LIBC_NAMESPACE::powf(inf, pos_non_integer)); + EXPECT_FP_EQ(inf, LIBC_NAMESPACE::powf(inf, one_half)); EXPECT_FP_EQ(inf, LIBC_NAMESPACE::powf(inf, inf)); EXPECT_FP_EQ(zero, LIBC_NAMESPACE::powf(inf, neg_inf)); EXPECT_FP_IS_NAN(LIBC_NAMESPACE::powf(inf, aNaN)); @@ -124,6 +128,7 @@ TEST_F(LlvmLibcPowfTest, SpecialNumbers) { EXPECT_FP_EQ(neg_inf, LIBC_NAMESPACE::powf(neg_inf, pos_odd_integer)); EXPECT_FP_EQ(inf, LIBC_NAMESPACE::powf(neg_inf, pos_even_integer)); EXPECT_FP_EQ(inf, LIBC_NAMESPACE::powf(neg_inf, pos_non_integer)); + EXPECT_FP_EQ(inf, LIBC_NAMESPACE::powf(neg_inf, one_half)); EXPECT_FP_EQ(inf, LIBC_NAMESPACE::powf(neg_inf, inf)); EXPECT_FP_EQ(zero, LIBC_NAMESPACE::powf(neg_inf, neg_inf)); EXPECT_FP_IS_NAN(LIBC_NAMESPACE::powf(neg_inf, aNaN));