[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