[libc-commits] [libc] [llvm] [libc][math] Improve accuracy for exp*f float-only implementations. (PR #227881)

via libc-commits libc-commits at lists.llvm.org
Sun Oct 4 06:31:37 PDT 2026


https://github.com/lntue updated https://github.com/llvm/llvm-project/pull/227881

>From 81b7f32d299d91946021561e583e697fa6613af0 Mon Sep 17 00:00:00 2001
From: Tue Ly <lntue.h at gmail.com>
Date: Wed, 30 Sep 2026 21:39:55 +0000
Subject: [PATCH] [libc][math] Improve accuracy for exp*f float-only
 implementations.

---
 libc/src/__support/math/CMakeLists.txt        |  1 +
 libc/src/__support/math/exp2f_float_utils.h   | 25 ++++++++++++-------
 libc/src/__support/math/expf_float_eval.h     |  6 ++---
 .../llvm-project-overlay/libc/BUILD.bazel     |  1 +
 4 files changed, 21 insertions(+), 12 deletions(-)

diff --git a/libc/src/__support/math/CMakeLists.txt b/libc/src/__support/math/CMakeLists.txt
index ec8b8d9eee4e5..c6a1ad1909097 100644
--- a/libc/src/__support/math/CMakeLists.txt
+++ b/libc/src/__support/math/CMakeLists.txt
@@ -3104,6 +3104,7 @@ add_header_library(
   DEPENDS
     libc.src.__support.CPP.bit
     libc.src.__support.FPUtil.multiply_add
+    libc.src.__support.FPUtil.polyeval
     libc.src.__support.macros.config
     libc.src.__support.macros.optimization
 )
diff --git a/libc/src/__support/math/exp2f_float_utils.h b/libc/src/__support/math/exp2f_float_utils.h
index 1c96050bea37f..28bad5a92dd76 100644
--- a/libc/src/__support/math/exp2f_float_utils.h
+++ b/libc/src/__support/math/exp2f_float_utils.h
@@ -15,6 +15,7 @@
 #define LLVM_LIBC_SRC___SUPPORT_MATH_EXP2F_FLOAT_UTILS_H
 
 #include "src/__support/CPP/bit.h"
+#include "src/__support/FPUtil/PolyEval.h"
 #include "src/__support/FPUtil/multiply_add.h"
 #include "src/__support/macros/config.h"
 #include "src/__support/macros/optimization.h"
@@ -70,9 +71,8 @@ LIBC_INLINE float scale_exp2f(float y, int m) {
   return (y * scale) * 0x1.0p-25f;
 }
 
-// Degree-6 polynomial approximating 2^u - 1 = u * P(u) on [-0.5, 0.5] using
-// Estrin's scheme and exponent scaling.
-// Evaluates 2^x = 2^m * 2^u.
+// Degree-6 polynomial approximating 2^u - 1 = u * P(u) on [-0.5, 0.5] and
+// exponent scaling to evaluate 2^x = 2^m * 2^u.
 // Generated by Sollya with:
 // > display = hexadecimal;
 // > P = fpminimax((2^x - 1)/x, 5, [|SG...|], [-0.5, 0.5]);
@@ -80,6 +80,15 @@ LIBC_INLINE float scale_exp2f(float y, int m) {
 //   0x1.68cb...p-28
 // > dirtyinfnorm((1 + x * P - 2^x) / 2^x, [-0.5, 0.5]);
 //   0x1.5109...p-28
+//
+// Instead of using pure Estrin's scheme on P(u), we evaluate the degree-4
+// part p = c1 + c2 * u + c3 * u^2 + c4 * u^3 + c5 * u^4 with Estrin's
+// scheme, and then evaluate y = 1.0f + u * (c0 + u * p) with Horner's
+// scheme.
+// In pure Estrin's scheme, p0 = c0 + c1 * u, q0 = p0 + u^2 * p1, and
+// p = q0 + u^4 * p2 all round at the exponent of c0 (~2^-1), accumulating
+// rounding errors on non-FMA targets. Evaluating c1..c5 first keeps only a
+// single rounding at c0's scale.
 LIBC_INLINE float exp2f_eval(float u, int m) {
   constexpr float COEFFS[6] = {0x1.62e430p-1f,  0x1.ebfbdep-3f,
                                0x1.c6af9ep-5f,  0x1.3b2c58p-7f,
@@ -88,14 +97,12 @@ LIBC_INLINE float exp2f_eval(float u, int m) {
   float u2 = u * u;
   float u4 = u2 * u2;
 
-  float p0 = fputil::multiply_add(u, COEFFS[1], COEFFS[0]);
-  float p1 = fputil::multiply_add(u, COEFFS[3], COEFFS[2]);
-  float p2 = fputil::multiply_add(u, COEFFS[5], COEFFS[4]);
-
+  float p0 = fputil::multiply_add(u, COEFFS[2], COEFFS[1]);
+  float p1 = fputil::multiply_add(u, COEFFS[4], COEFFS[3]);
   float q0 = fputil::multiply_add(u2, p1, p0);
-  float p = fputil::multiply_add(u4, p2, q0);
+  float p = fputil::multiply_add(u4, COEFFS[5], q0);
 
-  float y = fputil::multiply_add(u, p, 1.0f);
+  float y = fputil::polyeval(u, 1.0f, COEFFS[0], p);
 
   return scale_exp2f(y, m);
 }
diff --git a/libc/src/__support/math/expf_float_eval.h b/libc/src/__support/math/expf_float_eval.h
index e479cb58d2fea..ab8ad649c539c 100644
--- a/libc/src/__support/math/expf_float_eval.h
+++ b/libc/src/__support/math/expf_float_eval.h
@@ -100,11 +100,11 @@ LIBC_INLINE float expf(float x) {
   // Constants generated by Sollya with:
   // > display = hexadecimal;
   // > LOG2_E = round(log2(exp(1)), SG, RN);
-  // > LOG_2_HI = round(log(2), 12, RN);
+  // > LOG_2_HI = round(log(2), 16, RN);
   // > LOG_2_LO = round(log(2) - LOG_2_HI, SG, RN);
   constexpr float LOG2_E = 0x1.715476p+0f;
-  constexpr float LOG_2_HI = 0x1.62ep-1f;
-  constexpr float LOG_2_LO = 0x1.0bfbe8p-15f;
+  constexpr float LOG_2_HI = 0x1.62e4p-1f;
+  constexpr float LOG_2_LO = 0x1.7f7d1cp-20f;
 
   float kf = fputil::nearest_integer(x * LOG2_E);
   int k = static_cast<int>(kf);
diff --git a/utils/bazel/llvm-project-overlay/libc/BUILD.bazel b/utils/bazel/llvm-project-overlay/libc/BUILD.bazel
index b6ec7c595fced..f2bd619962b39 100644
--- a/utils/bazel/llvm-project-overlay/libc/BUILD.bazel
+++ b/utils/bazel/llvm-project-overlay/libc/BUILD.bazel
@@ -8701,6 +8701,7 @@ libc_support_library(
         ":__support_common",
         ":__support_cpp_bit",
         ":__support_fputil_multiply_add",
+        ":__support_fputil_polyeval",
         ":__support_macros_config",
         ":__support_macros_optimization",
     ],



More information about the libc-commits mailing list