[libc-commits] [libc] [libc][math] Fix pow() subnormal base exponent computation (PR #198134)
via libc-commits
libc-commits at lists.llvm.org
Sat May 16 17:14:26 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-libc
Author: Nico Weber (nico)
<details>
<summary>Changes</summary>
For subnormal inputs, get_exponent() returns -1023. The code subtracted 64 after normalizing but didn't recompute e_x from the normalized value. This set e_x to -1087 for every subnormal.
To fix, compute e_x from the normalized value.
powf() doesn't have this bug because it adds
`x_u >> FloatBits::FRACTION_LEN` to ex, where x_u is `x_u = FloatBits(x).uintval();` with `x` being the normalized value. Added subnormal base tests for powf to show that it works fine as-is.
Fixes #<!-- -->197212.
---
Full diff: https://github.com/llvm/llvm-project/pull/198134.diff
3 Files Affected:
- (modified) libc/src/__support/math/pow.h (+3-2)
- (modified) libc/test/src/math/smoke/pow_test.cpp (+6)
- (modified) libc/test/src/math/smoke/powf_test.cpp (+7)
``````````diff
diff --git a/libc/src/__support/math/pow.h b/libc/src/__support/math/pow.h
index eebbd72bd31ca..27e382435ecb3 100644
--- a/libc/src/__support/math/pow.h
+++ b/libc/src/__support/math/pow.h
@@ -340,8 +340,9 @@ LIBC_INLINE double pow(double x, double y) {
// Normalize denormal inputs.
if (x_a < FPBits::min_normal().uintval()) {
- e_x -= 64.0;
- x_mant = FPBits(x * 0x1.0p64).get_mantissa();
+ FPBits x_norm(x * 0x1.0p64);
+ e_x = static_cast<double>(x_norm.get_exponent()) - 64.0;
+ x_mant = x_norm.get_mantissa();
}
// x is finite and negative, and y is a finite integer.
diff --git a/libc/test/src/math/smoke/pow_test.cpp b/libc/test/src/math/smoke/pow_test.cpp
index b27134aca45d8..ab8a0cf75294b 100644
--- a/libc/test/src/math/smoke/pow_test.cpp
+++ b/libc/test/src/math/smoke/pow_test.cpp
@@ -229,6 +229,12 @@ TEST_F(LlvmLibcPowTest, SpecialNumbers) {
}
}
+TEST_F(LlvmLibcPowTest, SubnormalBase) {
+ EXPECT_FP_EQ(0x1.0p825, LIBC_NAMESPACE::pow(0x1.0p-1024, -0x1.9c8p-1));
+ EXPECT_FP_EQ(0x1.0p-512, LIBC_NAMESPACE::pow(0x1.0p-1024, 0.5));
+ EXPECT_FP_EQ(0x1.0p512, LIBC_NAMESPACE::pow(0x1.0p-1024, -0.5));
+}
+
#ifdef LIBC_TEST_FTZ_DAZ
using namespace LIBC_NAMESPACE::testing;
diff --git a/libc/test/src/math/smoke/powf_test.cpp b/libc/test/src/math/smoke/powf_test.cpp
index 76e8e8cee7a98..65c4da35b6db0 100644
--- a/libc/test/src/math/smoke/powf_test.cpp
+++ b/libc/test/src/math/smoke/powf_test.cpp
@@ -236,6 +236,13 @@ TEST_F(LlvmLibcPowfTest, SpecialNumbers) {
EXPECT_FP_EQ(0.0f, LIBC_NAMESPACE::powf(-0.015625f, 26.0f));
}
+TEST_F(LlvmLibcPowfTest, SubnormalBase) {
+ EXPECT_FP_EQ(0x1.0p-32f, LIBC_NAMESPACE::powf(0x1.0p-128f, 0.25f));
+ EXPECT_FP_EQ(0x1.0p96f, LIBC_NAMESPACE::powf(0x1.0p-128f, -0.75f));
+ EXPECT_FP_EQ(0x1.90a962p-33f, LIBC_NAMESPACE::powf(0x1.8p-130f, 0.25f));
+ EXPECT_FP_EQ(0x1.47238cp+32f, LIBC_NAMESPACE::powf(0x1.8p-130f, -0.25f));
+}
+
#ifdef LIBC_TEST_FTZ_DAZ
using namespace LIBC_NAMESPACE::testing;
``````````
</details>
https://github.com/llvm/llvm-project/pull/198134
More information about the libc-commits
mailing list