[libc-commits] [libc] [libc][math] Remove unneccessary checks and speedup for pow_fast under LIBC_MATH_HAS_SMALL_TABLES. (PR #232623)
via libc-commits
libc-commits at lists.llvm.org
Thu Oct 15 23:57:20 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-libc
Author: lntue
<details>
<summary>Changes</summary>
This reduces code size by about 2.5 KB.
---
Full diff: https://github.com/llvm/llvm-project/pull/232623.diff
3 Files Affected:
- (modified) libc/src/__support/math/pow_fast.h (+7-1)
- (modified) libc/src/__support/math/pow_utils.h (+93-79)
- (modified) libc/test/src/math/smoke/pow_test.cpp (+16-10)
``````````diff
diff --git a/libc/src/__support/math/pow_fast.h b/libc/src/__support/math/pow_fast.h
index 5960bea60642b..fb8d14bea8dfe 100644
--- a/libc/src/__support/math/pow_fast.h
+++ b/libc/src/__support/math/pow_fast.h
@@ -36,14 +36,17 @@ LIBC_INLINE double pow(double x, double y) {
FPBits xbits(x), ybits(y);
uint64_t x_u = xbits.uintval();
- uint64_t y_u = ybits.uintval();
uint64_t y_a = ybits.abs().uintval();
+#ifndef LIBC_MATH_HAS_SMALL_TABLES
+ uint64_t y_u = ybits.uintval();
+
if (LIBC_UNLIKELY((x_u & 0x0003'FFFF'FFFF'FFFF) == 0 ||
(y_u & 0x000F'FFFF'FFFF'FFFF) == 0)) {
if (auto r = check_special_inputs(x, y); LIBC_UNLIKELY(r.has_value()))
return r.value();
}
+#endif // !LIBC_MATH_HAS_SMALL_TABLES
double e_x = static_cast<double>(xbits.get_exponent());
uint64_t x_mant = xbits.get_mantissa();
@@ -51,6 +54,9 @@ LIBC_INLINE double pow(double x, double y) {
double sign_d = 1.0;
if (LIBC_UNLIKELY(y_a <= Y_LOWER_BOUND || y_a >= Y_UPPER_BOUND ||
+#ifdef LIBC_MATH_HAS_SMALL_TABLES
+ x_u == FPBits::one().uintval() ||
+#endif // LIBC_MATH_HAS_SMALL_TABLES
x_u >= FPBits::inf().uintval() ||
x_u < FPBits::min_normal().uintval())) {
if (auto r = check_exceptional_cases(x, y, e_x, x_mant, is_neg, sign_d);
diff --git a/libc/src/__support/math/pow_utils.h b/libc/src/__support/math/pow_utils.h
index 83eba6007e20f..305dbc67590be 100644
--- a/libc/src/__support/math/pow_utils.h
+++ b/libc/src/__support/math/pow_utils.h
@@ -434,101 +434,115 @@ check_exceptional_cases(double &x, double &y, double &e_x, uint64_t &x_mant,
FPBits x_abs = xbits.abs();
FPBits y_abs = ybits.abs();
- uint64_t x_u = xbits.uintval();
uint64_t y_mant = ybits.get_mantissa();
uint64_t x_a = x_abs.uintval();
uint64_t y_a = y_abs.uintval();
- if (LIBC_UNLIKELY(y_a <= Y_LOWER_BOUND || y_a >= Y_UPPER_BOUND ||
- x_u >= FPBits::inf().uintval() ||
- x_u < FPBits::min_normal().uintval())) {
- // If x or y is signaling NaN
- if (x_abs.is_signaling_nan() || y_abs.is_signaling_nan()) {
- fputil::raise_except_if_required(FE_INVALID);
- return FPBits::quiet_nan().get_val();
- }
+ // If x or y is signaling NaN
+ if (x_abs.is_signaling_nan() || y_abs.is_signaling_nan()) {
+ fputil::raise_except_if_required(FE_INVALID);
+ return FPBits::quiet_nan().get_val();
+ }
- // |y| > |1075 / log2(1 - 2^-53)|.
- if (y_a > 0x43d7'4910'd52d'3052) {
- if (y_a >= 0x7ff0'0000'0000'0000) {
- // y is inf or nan
- if (y_mant != 0) {
- // y is NaN
- // pow(1, NaN) = 1 is already dealt with.
- // pow(x, NaN) = NaN
- return y;
- }
-
- // Now y is +-Inf
- if (x_abs.is_nan()) {
- // pow(NaN, +-Inf) = NaN
- return x;
- }
-
- if (x_a == 0x3ff0'0000'0000'0000) {
- // pow(+-1, +-Inf) = 1.0
- return 1.0;
- }
-
- // pow (|x| < 1, -inf) = +inf
- // pow (|x| < 1, +inf) = 0.0
- // pow (|x| > 1, -inf) = 0.0
- // pow (|x| > 1, +inf) = +inf
- return ((x_a < FPBits::one().uintval()) == y_sign)
- ? FPBits::inf().get_val()
- : 0.0;
+#ifdef LIBC_MATH_HAS_SMALL_TABLES
+ if (x == 1.0 || y == 0.0)
+ return 1.0;
+#endif // LIBC_MATH_HAS_SMALL_TABLES
+
+ // |y| > |1075 / log2(1 - 2^-53)|.
+ if (y_a > Y_UPPER_BOUND) {
+ if (y_a >= FPBits::inf().uintval()) {
+ // y is inf or nan
+ if (y_mant != 0) {
+ // y is NaN
+ // pow(1, NaN) = 1 is already dealt with.
+ // pow(x, NaN) = NaN
+ return y;
}
- // x^y will overflow / underflow in double precision. Set y to a
- // large enough exponent but not too large, so that the computations
- // won't overflow in double precision.
- y = y_sign ? -0x1.0p100 : 0x1.0p100;
- }
- // y is finite and non-zero.
+ // Now y is +-Inf
+ if (x_abs.is_nan()) {
+ // pow(NaN, +-Inf) = NaN
+ return x;
+ }
- if (x_a == FPBits::inf().uintval()) {
- bool out_is_neg = x_sign && is_odd_integer(y);
- Sign out_sign = out_is_neg ? Sign::NEG : Sign::POS;
- return y_sign ? FPBits::zero(out_sign).get_val()
- : FPBits::inf(out_sign).get_val();
- }
+ if (x_a == FPBits::one().uintval()) {
+ // pow(+-1, +-Inf) = 1.0
+ return 1.0;
+ }
- if (x_a > FPBits::inf().uintval()) {
- // x is NaN.
- // pow (aNaN, 0) is already taken care above.
- return x;
+ // pow (|x| < 1, -inf) = +inf
+ // pow (|x| < 1, +inf) = 0.0
+ // pow (|x| > 1, -inf) = 0.0
+ // pow (|x| > 1, +inf) = +inf
+ return ((x_a < FPBits::one().uintval()) == y_sign)
+ ? FPBits::inf().get_val()
+ : 0.0;
}
+ // x^y will overflow / underflow in double precision. Set y to a
+ // large enough exponent but not too large, so that the computations
+ // won't overflow in double precision.
+ y = y_sign ? -0x1.0p100 : 0x1.0p100;
+ }
- // Normalize denormal inputs.
- if (x_a < FPBits::min_normal().uintval()) {
- FPBits x_norm(x * 0x1.0p64);
- e_x = static_cast<double>(x_norm.get_exponent()) - 64.0;
- x_mant = x_norm.get_mantissa();
- }
+ // y is finite and non-zero.
+ if (x_a > FPBits::inf().uintval()) {
+ // x is NaN.
+ // pow (aNaN, 0) is already taken care above.
+ return x;
+ }
- // x is finite and negative, and y is a finite integer.
- if (x_sign) {
- if (is_integer(y)) {
- x = -x;
- if (is_odd_integer(y)) {
- is_neg = true;
- sign_d = -1.0;
- }
- } else {
- // pow( negative, non-integer ) = NaN
- fputil::set_errno_if_required(EDOM);
- fputil::raise_except_if_required(FE_INVALID);
- return FPBits::quiet_nan().get_val();
+ if (x_sign) {
+ if (is_integer(y)) {
+ x = -x;
+ if (is_odd_integer(y)) {
+ is_neg = true;
+ sign_d = -1.0;
}
+ } else if (x != 0.0 && x_a < FPBits::inf().uintval()) {
+ // pow( negative, non-integer ) = NaN
+ fputil::set_errno_if_required(EDOM);
+ fputil::raise_except_if_required(FE_INVALID);
+ return FPBits::quiet_nan().get_val();
}
+ }
- if (y_a <= Y_LOWER_BOUND) {
- volatile double one = 1.0;
- volatile double eps = ((x_a < FPBits::one().uintval()) == !y_sign)
- ? -0x1.0p-100
- : 0x1.0p-100;
- return one + eps;
+#ifdef LIBC_MATH_HAS_SMALL_TABLES
+ if (x == 0.0) {
+ if (y_sign) {
+ // pow(0, negative number) = inf
+ fputil::set_errno_if_required(EDOM);
+ fputil::raise_except_if_required(FE_DIVBYZERO);
+ return is_neg ? -FPBits::inf().get_val() : FPBits::inf().get_val();
}
+ // pow(0, positive number) = 0
+ return is_neg ? -0.0 : 0.0;
+ }
+#endif // LIBC_MATH_HAS_SMALL_TABLES
+
+ if (x_a == FPBits::inf().uintval()) {
+ if (y_sign)
+ return is_neg ? -0.0 : 0.0;
+ return is_neg ? -FPBits::inf().get_val() : FPBits::inf().get_val();
+ }
+
+ if (y_a <= Y_LOWER_BOUND) {
+#if defined(LIBC_MATH_HAS_NO_EXCEPT) && \
+ defined(LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY)
+ return 1.0;
+#else
+ volatile double one = 1.0;
+ volatile double eps =
+ ((x_a < FPBits::one().uintval()) == !y_sign) ? -0x1.0p-100 : 0x1.0p-100;
+ return one + eps;
+#endif // LIBC_MATH_HAS_NO_EXCEPT && LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
+ }
+
+ // Normalize denormal inputs.
+ if (x_a < FPBits::min_normal().uintval()) {
+ FPBits x_norm(x * 0x1.0p64);
+ e_x = static_cast<double>(x_norm.get_exponent()) - 64.0;
+ x_mant = x_norm.get_mantissa();
}
return cpp::nullopt;
diff --git a/libc/test/src/math/smoke/pow_test.cpp b/libc/test/src/math/smoke/pow_test.cpp
index 9e7599d9546d9..61b3f6d017919 100644
--- a/libc/test/src/math/smoke/pow_test.cpp
+++ b/libc/test/src/math/smoke/pow_test.cpp
@@ -206,16 +206,22 @@ TEST_F(LlvmLibcPowTest, SpecialNumbers) {
// Exact powers of 10:
EXPECT_FP_EQ(1.0, LIBC_NAMESPACE::pow(10.0, 0.0));
- EXPECT_FP_EQ(10.0, LIBC_NAMESPACE::pow(10.0, 1.0));
- EXPECT_FP_EQ(100.0, LIBC_NAMESPACE::pow(10.0, 2.0));
- EXPECT_FP_EQ(1000.0, LIBC_NAMESPACE::pow(10.0, 3.0));
- EXPECT_FP_EQ(10000.0, LIBC_NAMESPACE::pow(10.0, 4.0));
- EXPECT_FP_EQ(100000.0, LIBC_NAMESPACE::pow(10.0, 5.0));
- EXPECT_FP_EQ(1000000.0, LIBC_NAMESPACE::pow(10.0, 6.0));
- EXPECT_FP_EQ(10000000.0, LIBC_NAMESPACE::pow(10.0, 7.0));
- EXPECT_FP_EQ(100000000.0, LIBC_NAMESPACE::pow(10.0, 8.0));
- EXPECT_FP_EQ(1000000000.0, LIBC_NAMESPACE::pow(10.0, 9.0));
- EXPECT_FP_EQ(10000000000.0, LIBC_NAMESPACE::pow(10.0, 10.0));
+#ifdef LIBC_MATH_HAS_SKIP_ACCURATE_PASS
+ if (ROUNDING_MODES[i] == RoundingMode::Nearest) {
+#endif // LIBC_MATH_HAS_SKIP_ACCURATE_PASS
+ EXPECT_FP_EQ(10.0, LIBC_NAMESPACE::pow(10.0, 1.0));
+ EXPECT_FP_EQ(100.0, LIBC_NAMESPACE::pow(10.0, 2.0));
+ EXPECT_FP_EQ(1000.0, LIBC_NAMESPACE::pow(10.0, 3.0));
+ EXPECT_FP_EQ(10000.0, LIBC_NAMESPACE::pow(10.0, 4.0));
+ EXPECT_FP_EQ(100000.0, LIBC_NAMESPACE::pow(10.0, 5.0));
+ EXPECT_FP_EQ(1000000.0, LIBC_NAMESPACE::pow(10.0, 6.0));
+ EXPECT_FP_EQ(10000000.0, LIBC_NAMESPACE::pow(10.0, 7.0));
+ EXPECT_FP_EQ(100000000.0, LIBC_NAMESPACE::pow(10.0, 8.0));
+ EXPECT_FP_EQ(1000000000.0, LIBC_NAMESPACE::pow(10.0, 9.0));
+ EXPECT_FP_EQ(10000000000.0, LIBC_NAMESPACE::pow(10.0, 10.0));
+#ifdef LIBC_MATH_HAS_SKIP_ACCURATE_PASS
+ }
+#endif // LIBC_MATH_HAS_SKIP_ACCURATE_PASS
// Overflow / Underflow:
if (ROUNDING_MODES[i] != RoundingMode::Downward &&
``````````
</details>
https://github.com/llvm/llvm-project/pull/232623
More information about the libc-commits
mailing list