[libc-commits] [libc] [llvm] [libc][math] Optimize integer-only expf and add integer-only exp2f and exp10f (PR #228898)

via libc-commits libc-commits at lists.llvm.org
Sun Oct 4 07:58:26 PDT 2026


https://github.com/lntue created https://github.com/llvm/llvm-project/pull/228898


- Optimize `expf_integer_eval` for code size and performance, and share the core `2^u` evaluation and fixed-point scaling (`src/__support/math/exp2f_integer_utils.h`) with new integer-only implementations for `exp2f` and `exp10f` (`src/__support/math/exp2f_integer_eval.h` and `src/__support/math/exp10f_integer_eval.h`).
- Expose `expf`, `exp2f`, and `exp10f` with explicit rounding mode parameters under `LIBC_NAMESPACE::shared::static_rounding`.
- Key optimizations for 32-bit targets (where `LIBC_TYPES_HAS_INT128` is not defined):
  - Specialize single-word (`N == 1`) `multiword::quick_mul_hi` in `src/__support/big_int.h` to use a 3-multiply half-word high product when a native double-width integer type is unavailable, and mark the `quick_mul_hi` / `Frac64` fast path `LIBC_ALWAYS_INLINE` to prevent helper outlining at `-Oz`.
  - Evaluate the degree-11 minimax polynomial with a `LIBC_LOOP_NOUNROLL` Horner loop on 32-bit targets (and a 2-way Estrin/Horner scheme when `__uint128_t` is available).
  - Compute `|x| * log2(b)` using a 32x64 -> 64 high product before shifting by `s in [0, 31]`, avoiding 64x64 multiplies and `__aeabi_llsr` calls.
  - Use two's complement negation before floor/fraction extraction and unify normal/subnormal output reconstruction into a single 32-bit shift.
- On 32-bit ARM (`armv8m.main-none-eabi` / `armv7m-none-eabi`), this reduces `expf_integer_eval` code size (`.text + .rodata`) from 1,096 - 1,938 B down to 356 - 362 B (and 890 - 922 B combined for all three `expf`, `exp2f`, and `exp10f` functions), with zero soft-FP runtime dependencies.
- Update smoke, unit, exhaustive, and shared math tests to cover all variants.

### Code Size (`text + rodata`, `clang++-21` / `llvm-size-21`)                          
                                                                                        
| Target / Function | Implementation | `-O2` | `-O3` | `-Os` | `-Oz` | Soft-FP |        
| :--- | :--- | ---: | ---: | ---: | ---: | :--- |                                      
| `expf` (`armv8m`) | **`integer_eval` (PR)** | **362B** | **362B** | **362B** |  **356B** | **0B** |                                                                       
| | `integer_eval` (old) | 1134B | 1198B | 1938B | 1188B | 0B |                         
| | `float_eval` | 440B | 460B | 428B | 468B | 0B |                                     
| | Picolibc `expf` | 544B | 548B | 536B | 508B | 0B |                                  
| `expf` (`armv7m`) | **`integer_eval` (PR)** | **362B** | **362B** | **362B** |  **356B** | **0B** |                                                                       
| | `integer_eval` (old) | 1096B | 1100B | 1908B | 1152B | 0B |                         
| | `float_eval` | 546B | 558B | 548B | 556B | +1.1–1.7KB |                             
| | Picolibc `expf` | 592B | 620B | 590B | 558B | +1.6–2.2KB |                          
| `exp2f` (`v8m`/`v7m`) | **`integer_eval` (PR)** | **346B** | **346B** | **346B** | **340B** | **0B** |                                                                       
| | `float_eval` | 400/476B | 420/488B | 388/458B | 432/476B | +1.1–1.7KB |             
| `exp10f` (`v8m`/`v7m`) | **`integer_eval` (PR)** | **386B** | **386B** | **386B** | **376B** | **0B** |                                                                       
| | `float_eval` | 728/778B | 748/790B | 672/792B | 660/740B | +1.1–1.7KB |             
| **All 3 combined** | **`integer_eval` (PR)** | **922B** | **922B** | **914B** | **890B** | **0B** |
| (`expf`+`exp2f`+`exp10f`) | `float_eval` | 1568/1800B | 1628/1836B | 1488/1798B | 1032/1060B | +1.1–1.7KB |

### Performance on 32-Bit ARM (Dynamic Instructions / Estimated Cycles)

| Target | Implementation (`expf`) | `-O2` | `-O3` | `-Os` | `-Oz` | Relative |         
| :--- | :--- | ---: | ---: | ---: | ---: | :--- |
| **`armv7m`** | **`integer_eval` (PR)** | **210/271c** | **210/271c** | **210/271c** | **180/256c** | **1.0x** |
| *(soft-FP)* | `integer_eval` (old) | 251/284c | 251/284c | 720/953c | 2218/5057c | 1.1–19.7x slower |
| | Picolibc (ASM soft-FP) | 785/956c | 785/955c | 786/957c | 777/979c | 3.5–3.8x slower |
| | `float_eval` (ASM soft-FP) | 1029/1293c | 1030/1295c | 1028/1292c | 1029/1334c | 4.8–5.2x slower |
| | Picolibc (C soft-FP) | 1799/2577c | 1798/2563c | 1779/2510c | 1733/2571c | 9.3–10.0x slower |
| | `float_eval` (C soft-FP) | 2201/3195c | 2203/3186c | 2170/3105c | 2110/3173c | 11.5–12.4x slower |
| **`armv8m`** | **`integer_eval` (PR)** | **210/271c** | **210/271c** | **210/271c** | **180/256c** | **1.0x (0 FPU)** |
| *(32b FPU)* | `float_eval` (HW FPU) | 69/110c | 68/107c | 70/113c | 83/142c | 1.8–2.5x faster |
| | Picolibc (HW FPU) | 71/103c | 71/103c | 69/104c | 60/99c | 2.6x faster |            

Assisted-by: Gemini is used for code size analysis, performance tuning, and test refactoring.

>From 246aa4a69914ef0a619c91828319d4dd19b4ea1f Mon Sep 17 00:00:00 2001
From: Tue Ly <lntue.h at gmail.com>
Date: Sun, 4 Oct 2026 13:28:04 +0000
Subject: [PATCH] [libc][math] Optimize integer-only expf and add integer-only
 exp2f and exp10f.

- Optimize `expf_integer_eval` for code size and performance, and share the
  core `2^u` evaluation and fixed-point scaling (`src/__support/math/exp2f_integer_utils.h`)
  with new integer-only implementations for `exp2f` and `exp10f`
  (`src/__support/math/exp2f_integer_eval.h` and `src/__support/math/exp10f_integer_eval.h`).
- Expose `expf`, `exp2f`, and `exp10f` with explicit rounding mode parameters
  under `LIBC_NAMESPACE::shared::static_rounding`.
- Key optimizations for 32-bit targets (where `LIBC_TYPES_HAS_INT128` is not
  defined):
  - Specialize single-word (`N == 1`) `multiword::quick_mul_hi` in
    `src/__support/big_int.h` to use a 3-multiply half-word high product when
    a native double-width integer type is unavailable, and mark the
    `quick_mul_hi` / `Frac64` fast path `LIBC_ALWAYS_INLINE` to prevent
    helper outlining at `-Oz`.
  - Evaluate the degree-11 minimax polynomial with a `LIBC_LOOP_NOUNROLL`
    Horner loop on 32-bit targets (and a 2-way Estrin/Horner scheme when
    `__uint128_t` is available).
  - Compute `|x| * log2(b)` using a 32x64 -> 64 high product before shifting
    by `s in [0, 31]`, avoiding 64x64 multiplies and `__aeabi_llsr` calls.
  - Use two's complement negation before floor/fraction extraction and unify
    normal/subnormal output reconstruction into a single 32-bit shift.
- On 32-bit ARM (`armv8m.main-none-eabi` / `armv7m-none-eabi`), this reduces
  `expf_integer_eval` code size (`.text + .rodata`) from 1,096 - 1,938 B down
  to 356 - 362 B (and 890 - 922 B combined for all three `expf`, `exp2f`, and
  `exp10f` functions), with zero soft-FP runtime dependencies.
- Update smoke, unit, exhaustive, and shared math tests to cover all variants.

Assisted-by: Gemini is used for code size analysis, performance tuning, and test refactoring.
---
 libc/shared/math/static_rounding/exp10f.h     |  30 ++
 libc/shared/math/static_rounding/exp2f.h      |  30 ++
 libc/shared/math/static_rounding/expf.h       |   2 +
 libc/shared/static_rounding_math.h            |   2 +
 libc/src/__support/big_int.h                  |  59 +++-
 libc/src/__support/frac64.h                   |  30 +-
 libc/src/__support/math/CMakeLists.txt        |  55 +++-
 libc/src/__support/math/exp10f.h              |   8 +
 libc/src/__support/math/exp10f_integer_eval.h | 148 ++++++++++
 libc/src/__support/math/exp2f.h               |   8 +
 libc/src/__support/math/exp2f_integer_eval.h  | 125 ++++++++
 libc/src/__support/math/exp2f_integer_utils.h | 163 ++++++++++
 libc/src/__support/math/expf_integer_eval.h   | 279 ++++--------------
 libc/test/shared/CMakeLists.txt               |   1 +
 libc/test/shared/shared_math_test.cpp         | 103 ++++++-
 libc/test/src/__support/big_int_test.cpp      |   1 +
 libc/test/src/math/CMakeLists.txt             |   2 +
 libc/test/src/math/exhaustive/CMakeLists.txt  |   6 +
 libc/test/src/math/exhaustive/exp10f_test.cpp |  18 ++
 libc/test/src/math/exhaustive/exp2f_test.cpp  |  18 ++
 libc/test/src/math/exhaustive/expf_test.cpp   |   2 +-
 libc/test/src/math/exp10f_test.cpp            |  53 +++-
 libc/test/src/math/exp2f_test.cpp             |  61 ++--
 libc/test/src/math/expf_test.cpp              |   2 +-
 libc/test/src/math/smoke/CMakeLists.txt       |   2 +
 libc/test/src/math/smoke/exp10f_test.cpp      |  52 +++-
 libc/test/src/math/smoke/exp2f_test.cpp       |  52 +++-
 libc/test/src/math/smoke/expf_test.cpp        |   2 +-
 .../llvm-project-overlay/libc/BUILD.bazel     |  80 ++++-
 .../libc/test/src/math/BUILD.bazel            |   2 +
 .../libc/test/src/math/smoke/BUILD.bazel      |   2 +
 31 files changed, 1076 insertions(+), 322 deletions(-)
 create mode 100644 libc/shared/math/static_rounding/exp10f.h
 create mode 100644 libc/shared/math/static_rounding/exp2f.h
 create mode 100644 libc/src/__support/math/exp10f_integer_eval.h
 create mode 100644 libc/src/__support/math/exp2f_integer_eval.h
 create mode 100644 libc/src/__support/math/exp2f_integer_utils.h

diff --git a/libc/shared/math/static_rounding/exp10f.h b/libc/shared/math/static_rounding/exp10f.h
new file mode 100644
index 0000000000000..923e6cc67960d
--- /dev/null
+++ b/libc/shared/math/static_rounding/exp10f.h
@@ -0,0 +1,30 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file contains the shared statically-rounded exp10f(x) function
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SHARED_MATH_STATIC_ROUNDING_EXP10F_H
+#define LLVM_LIBC_SHARED_MATH_STATIC_ROUNDING_EXP10F_H
+
+#include "shared/libc_common.h"
+#include "src/__support/math/exp10f_integer_eval.h"
+
+namespace LIBC_NAMESPACE_DECL {
+namespace shared {
+namespace static_rounding {
+
+using math::static_rounding::exp10f;
+
+} // namespace static_rounding
+} // namespace shared
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SHARED_MATH_STATIC_ROUNDING_EXP10F_H
diff --git a/libc/shared/math/static_rounding/exp2f.h b/libc/shared/math/static_rounding/exp2f.h
new file mode 100644
index 0000000000000..142031e4daf9a
--- /dev/null
+++ b/libc/shared/math/static_rounding/exp2f.h
@@ -0,0 +1,30 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file contains the shared statically-rounded exp2f(x) function
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SHARED_MATH_STATIC_ROUNDING_EXP2F_H
+#define LLVM_LIBC_SHARED_MATH_STATIC_ROUNDING_EXP2F_H
+
+#include "shared/libc_common.h"
+#include "src/__support/math/exp2f_integer_eval.h"
+
+namespace LIBC_NAMESPACE_DECL {
+namespace shared {
+namespace static_rounding {
+
+using math::static_rounding::exp2f;
+
+} // namespace static_rounding
+} // namespace shared
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SHARED_MATH_STATIC_ROUNDING_EXP2F_H
diff --git a/libc/shared/math/static_rounding/expf.h b/libc/shared/math/static_rounding/expf.h
index 0f4aeef673ba7..78dd010f7be43 100644
--- a/libc/shared/math/static_rounding/expf.h
+++ b/libc/shared/math/static_rounding/expf.h
@@ -19,9 +19,11 @@
 
 namespace LIBC_NAMESPACE_DECL {
 namespace shared {
+namespace static_rounding {
 
 using math::static_rounding::expf;
 
+} // namespace static_rounding
 } // namespace shared
 } // namespace LIBC_NAMESPACE_DECL
 
diff --git a/libc/shared/static_rounding_math.h b/libc/shared/static_rounding_math.h
index 5488354a9b552..ee3baae081401 100644
--- a/libc/shared/static_rounding_math.h
+++ b/libc/shared/static_rounding_math.h
@@ -15,6 +15,8 @@
 #define LLVM_LIBC_SHARED_STATIC_ROUNDING_MATH_H
 
 #include "shared/libc_common.h"
+#include "shared/math/static_rounding/exp10f.h"
+#include "shared/math/static_rounding/exp2f.h"
 #include "shared/math/static_rounding/expf.h"
 
 #endif // LLVM_LIBC_SHARED_STATIC_ROUNDING_MATH_H
diff --git a/libc/src/__support/big_int.h b/libc/src/__support/big_int.h
index 3bc9833664c13..b21d23471bf68 100644
--- a/libc/src/__support/big_int.h
+++ b/libc/src/__support/big_int.h
@@ -50,7 +50,7 @@ template <typename T> struct DoubleWide final : cpp::array<T, 2> {
 };
 
 // Converts an unsigned value into a DoubleWide<half_width_t<T>>.
-template <typename T> LIBC_INLINE constexpr auto split(T value) {
+template <typename T> LIBC_ALWAYS_INLINE constexpr auto split(T value) {
   static_assert(cpp::is_unsigned_v<T>);
   using half_type = half_width_t<T>;
   return DoubleWide<half_type>(
@@ -59,19 +59,23 @@ template <typename T> LIBC_INLINE constexpr auto split(T value) {
 }
 
 // The low part of a DoubleWide value.
-template <typename T> LIBC_INLINE constexpr T lo(const DoubleWide<T> &value) {
+template <typename T>
+LIBC_ALWAYS_INLINE constexpr T lo(const DoubleWide<T> &value) {
   return value[0];
 }
 // The high part of a DoubleWide value.
-template <typename T> LIBC_INLINE constexpr T hi(const DoubleWide<T> &value) {
+template <typename T>
+LIBC_ALWAYS_INLINE constexpr T hi(const DoubleWide<T> &value) {
   return value[1];
 }
 // The low part of an unsigned value.
-template <typename T> LIBC_INLINE constexpr half_width_t<T> lo(T value) {
+template <typename T>
+LIBC_ALWAYS_INLINE constexpr half_width_t<T> lo(T value) {
   return lo(split(value));
 }
 // The high part of an unsigned value.
-template <typename T> LIBC_INLINE constexpr half_width_t<T> hi(T value) {
+template <typename T>
+LIBC_ALWAYS_INLINE constexpr half_width_t<T> hi(T value) {
   return hi(split(value));
 }
 
@@ -239,9 +243,40 @@ LIBC_INLINE constexpr word multiply_with_carry(cpp::array<word, O> &dst,
 }
 
 template <typename word, size_t N>
-LIBC_INLINE constexpr void quick_mul_hi(cpp::array<word, N> &dst,
-                                        const cpp::array<word, N> &lhs,
-                                        const cpp::array<word, N> &rhs) {
+LIBC_ALWAYS_INLINE constexpr void quick_mul_hi(cpp::array<word, N> &dst,
+                                               const cpp::array<word, N> &lhs,
+                                               const cpp::array<word, N> &rhs) {
+  if constexpr (N == 1) {
+    if constexpr (cpp::is_same_v<word, uint8_t>) {
+      dst[0] = hi<uint16_t>(uint16_t(lhs[0]) * uint16_t(rhs[0]));
+    } else if constexpr (cpp::is_same_v<word, uint16_t>) {
+      dst[0] = hi<uint32_t>(uint32_t(lhs[0]) * uint32_t(rhs[0]));
+    }
+#ifdef LIBC_TYPES_HAS_INT64
+    else if constexpr (cpp::is_same_v<word, uint32_t>) {
+      dst[0] = hi<uint64_t>(uint64_t(lhs[0]) * uint64_t(rhs[0]));
+    }
+#endif
+#ifdef LIBC_TYPES_HAS_INT128
+    else if constexpr (cpp::is_same_v<word, uint64_t>) {
+      dst[0] = hi<__uint128_t>(__uint128_t(lhs[0]) * __uint128_t(rhs[0]));
+    }
+#endif
+    else {
+      // Split the single word into half-words and perform 3 half-word
+      // multiplications instead of 4.
+      word a = lhs[0];
+      word b = rhs[0];
+      word a_lo = lo(a);
+      word a_hi = hi(a);
+      word b_lo = lo(b);
+      word b_hi = hi(b);
+      word t1 = a_hi * b_lo;
+      word t2 = a_lo * b_hi + lo(t1);
+      dst[0] = a_hi * b_hi + hi(t1) + hi(t2);
+    }
+    return;
+  }
   Accumulator<word> acc;
   word carry = 0;
   // First round of accumulation for those at N - 1 in the full product.
@@ -638,7 +673,10 @@ struct BigInt {
   // `Bits` least significant bits of the full product, while this function will
   // approximate `Bits` most significant bits of the full product with errors
   // bounded by:
-  //   0 <= (a.full_mul(b) >> Bits) - a.quick_mul_hi(b)) <= WORD_COUNT - 1.
+  //   0 <= (a.full_mul(b) >> Bits) - a.quick_mul_hi(b)) <= max(WORD_COUNT - 1, 1)
+  // (when WORD_COUNT == 1 and WordType is natively supported by a double-width
+  // integer type, the error is 0; otherwise when split into half-words, the
+  // error is bounded by 1).
   //
   // An example usage of this is to quickly (but less accurately) compute the
   // product of (normalized) mantissas of floating point numbers:
@@ -651,11 +689,12 @@ struct BigInt {
   // Performance summary:
   //   Number of 64-bit x 64-bit -> 128-bit multiplications performed.
   //   Bits  WORD_COUNT  ful_mul  quick_mul_hi  Error bound
+  //     64      1         1           1            0 (1 without int128)
   //    128      2         4           3            1
   //    196      3         9           6            2
   //    256      4        16          10            3
   //    512      8        64          36            7
-  LIBC_INLINE constexpr BigInt quick_mul_hi(const BigInt &other) const {
+  LIBC_ALWAYS_INLINE constexpr BigInt quick_mul_hi(const BigInt &other) const {
     BigInt result{};
     multiword::quick_mul_hi(result.val, val, other.val);
     return result;
diff --git a/libc/src/__support/frac64.h b/libc/src/__support/frac64.h
index 350b0a8b701b5..00cb3f9077604 100644
--- a/libc/src/__support/frac64.h
+++ b/libc/src/__support/frac64.h
@@ -7,14 +7,15 @@
 //===----------------------------------------------------------------------===//
 ///
 /// \file
-/// This file contains the declaration of 64-bit unsigned fractional type
+/// This file contains the declaration of 64-bit unsigned fractional type.
 ///
 //===----------------------------------------------------------------------===//
 
 #ifndef LLVM_LIBC_SRC___SUPPORT_FRAC64_H
 #define LLVM_LIBC_SRC___SUPPORT_FRAC64_H
 
-#include "src/__support/big_int.h"
+#include "big_int.h"
+#include "src/__support/macros/attributes.h"
 #include "src/__support/macros/config.h"
 
 namespace LIBC_NAMESPACE_DECL {
@@ -22,41 +23,44 @@ namespace LIBC_NAMESPACE_DECL {
 struct Frac64 : public UInt<64> {
   using UInt<64>::UInt;
 
-  LIBC_INLINE constexpr Frac64 operator~() const { return Frac64(~val[0]); }
+  LIBC_ALWAYS_INLINE constexpr Frac64 operator~() const {
+    return Frac64(~val[0]);
+  }
 
-  LIBC_INLINE constexpr Frac64 operator+(Frac64 other) const {
+  LIBC_ALWAYS_INLINE constexpr Frac64 operator+(Frac64 other) const {
     return Frac64(val[0] + other.val[0]);
   }
 
-  LIBC_INLINE constexpr Frac64 operator-(Frac64 other) const {
+  LIBC_ALWAYS_INLINE constexpr Frac64 operator-(Frac64 other) const {
     return Frac64(val[0] - other.val[0]);
   }
 
-  LIBC_INLINE constexpr Frac64 operator*(Frac64 other) const {
-    UInt<64> r = UInt<64>::quick_mul_hi(UInt<64>(other));
-    return Frac64(r.val[0]);
+  // Fast hi part of the 128-bit product:
+  //   0 <= ((a * b) >> 64) - (Frac64(a) * Frac64(b)).val[0] <= 1.
+  LIBC_ALWAYS_INLINE constexpr Frac64 operator*(Frac64 other) const {
+    return Frac64(UInt<64>::quick_mul_hi(other).val);
   }
 
-  LIBC_INLINE constexpr Frac64 &operator+=(Frac64 other) {
+  LIBC_ALWAYS_INLINE constexpr Frac64 &operator+=(Frac64 other) {
     *this = *this + other;
     return *this;
   }
 
-  LIBC_INLINE constexpr Frac64 &operator-=(Frac64 other) {
+  LIBC_ALWAYS_INLINE constexpr Frac64 &operator-=(Frac64 other) {
     *this = *this - other;
     return *this;
   }
 
-  LIBC_INLINE constexpr Frac64 &operator*=(Frac64 other) {
+  LIBC_ALWAYS_INLINE constexpr Frac64 &operator*=(Frac64 other) {
     *this = *this * other;
     return *this;
   }
 
-  LIBC_INLINE constexpr Frac64 operator<<(size_t s) const {
+  LIBC_ALWAYS_INLINE constexpr Frac64 operator<<(size_t s) const {
     return Frac64(val[0] << s);
   }
 
-  LIBC_INLINE constexpr Frac64 operator>>(size_t s) const {
+  LIBC_ALWAYS_INLINE constexpr Frac64 operator>>(size_t s) const {
     return Frac64(val[0] >> s);
   }
 };
diff --git a/libc/src/__support/math/CMakeLists.txt b/libc/src/__support/math/CMakeLists.txt
index ec8b8d9eee4e5..cfc9d82287cf5 100644
--- a/libc/src/__support/math/CMakeLists.txt
+++ b/libc/src/__support/math/CMakeLists.txt
@@ -3108,6 +3108,21 @@ add_header_library(
     libc.src.__support.macros.optimization
 )
 
+add_header_library(
+  exp2f_integer_utils
+  HDRS
+    exp2f_integer_utils.h
+  DEPENDS
+    libc.hdr.fenv_macros
+    libc.src.__support.FPUtil.multiply_add
+    libc.src.__support.FPUtil.polyeval
+    libc.src.__support.frac64
+    libc.src.__support.macros.attributes
+    libc.src.__support.macros.config
+    libc.src.__support.macros.optimization
+    libc.src.__support.macros.properties.types
+)
+
 add_header_library(
   expf_double_eval
   HDRS
@@ -3163,12 +3178,14 @@ add_header_library(
   HDRS
     expf_integer_eval.h
   DEPENDS
-    .exp_constants
+    .exp2f_integer_utils
+    libc.hdr.fenv_macros
     libc.src.__support.CPP.bit
     libc.src.__support.FPUtil.fp_bits
+    libc.src.__support.frac64
+    libc.src.__support.macros.attributes
     libc.src.__support.macros.config
     libc.src.__support.macros.optimization
-    libc.src.__support.frac64
 )
 
 add_header_library(
@@ -4282,6 +4299,21 @@ add_header_library(
     libc.src.__support.macros.optimization
 )
 
+add_header_library(
+  exp2f_integer_eval
+  HDRS
+    exp2f_integer_eval.h
+  DEPENDS
+    .exp2f_integer_utils
+    libc.hdr.fenv_macros
+    libc.src.__support.CPP.bit
+    libc.src.__support.FPUtil.fp_bits
+    libc.src.__support.macros.attributes
+    libc.src.__support.macros.config
+    libc.src.__support.macros.optimization
+    libc.src.__support.macros.properties.types
+)
+
 add_header_library(
   exp2f
   HDRS
@@ -4289,9 +4321,11 @@ add_header_library(
   DEPENDS
     .exp2f_double_eval
     .exp2f_float_eval
+    .exp2f_integer_eval
     libc.src.__support.common
     libc.src.__support.macros.config
     libc.src.__support.macros.optimization
+    libc.src.__support.macros.properties.cpu_features
 )
 
 add_header_library(
@@ -4412,6 +4446,21 @@ add_header_library(
     libc.src.__support.macros.properties.cpu_features
 )
 
+add_header_library(
+  exp10f_integer_eval
+  HDRS
+    exp10f_integer_eval.h
+  DEPENDS
+    .exp2f_integer_utils
+    libc.hdr.fenv_macros
+    libc.src.__support.CPP.bit
+    libc.src.__support.FPUtil.fp_bits
+    libc.src.__support.frac64
+    libc.src.__support.macros.attributes
+    libc.src.__support.macros.config
+    libc.src.__support.macros.optimization
+)
+
 add_header_library(
   exp10f
   HDRS
@@ -4419,9 +4468,11 @@ add_header_library(
   DEPENDS
     .exp10f_double_eval
     .exp10f_float_eval
+    .exp10f_integer_eval
     libc.src.__support.common
     libc.src.__support.macros.config
     libc.src.__support.macros.optimization
+    libc.src.__support.macros.properties.cpu_features
 )
 
 add_header_library(
diff --git a/libc/src/__support/math/exp10f.h b/libc/src/__support/math/exp10f.h
index 67a9d24a5a2c2..2053d249d61c3 100644
--- a/libc/src/__support/math/exp10f.h
+++ b/libc/src/__support/math/exp10f.h
@@ -17,6 +17,7 @@
 #include "src/__support/common.h"
 #include "src/__support/macros/config.h"
 #include "src/__support/macros/optimization.h"
+#include "src/__support/macros/properties/cpu_features.h"
 
 #if defined(LIBC_MATH_HAS_SKIP_ACCURATE_PASS) &&                               \
     (defined(LIBC_MATH_HAS_SMALL_TABLES) ||                                    \
@@ -25,6 +26,13 @@
 #include "src/__support/math/exp10f_float_eval.h"
 #define LIBC_MATH_EXP10F_IMPL float_eval
 
+#elif !defined(LIBC_TARGET_CPU_HAS_FPU_DOUBLE) &&                              \
+    defined(LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY) &&                        \
+    defined(LIBC_MATH_HAS_NO_EXCEPT) && defined(LIBC_MATH_HAS_NO_ERRNO)
+
+#include "src/__support/math/exp10f_integer_eval.h"
+#define LIBC_MATH_EXP10F_IMPL integer_eval
+
 #else // !LIBC_MATH_HAS_SKIP_ACCURATE_PASS
 #include "src/__support/math/exp10f_double_eval.h"
 #define LIBC_MATH_EXP10F_IMPL double_eval
diff --git a/libc/src/__support/math/exp10f_integer_eval.h b/libc/src/__support/math/exp10f_integer_eval.h
new file mode 100644
index 0000000000000..6f41657a2c0e4
--- /dev/null
+++ b/libc/src/__support/math/exp10f_integer_eval.h
@@ -0,0 +1,148 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Integer-only implementation of exp10f.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC___SUPPORT_MATH_EXP10F_INTEGER_EVAL_H
+#define LLVM_LIBC_SRC___SUPPORT_MATH_EXP10F_INTEGER_EVAL_H
+
+#include "hdr/fenv_macros.h"
+#include "src/__support/CPP/bit.h"
+#include "src/__support/FPUtil/FPBits.h"
+#include "src/__support/frac64.h"
+#include "src/__support/macros/attributes.h"
+#include "src/__support/macros/config.h"
+#include "src/__support/macros/optimization.h"
+#include "src/__support/math/exp2f_integer_utils.h"
+
+namespace LIBC_NAMESPACE_DECL {
+namespace math {
+namespace static_rounding {
+
+// Constants generated by Sollya with:
+// > display = hexadecimal;
+// > round(log2(10) * 2^62, 64, RN);
+// LSB(LOG2_10) = 2^-62
+LIBC_INLINE_VAR constexpr Frac64 LOG2_10 = Frac64(0xd49a'784b'cd1b'8afe);
+
+LIBC_ALWAYS_INLINE uint32_t exp10f_bits(uint32_t x_u,
+                                        [[maybe_unused]] int rounding) {
+  using FPBits = fputil::FPBits<float>;
+
+  bool is_neg = (x_u >> 31) != 0;
+  uint32_t x_abs = x_u & 0x7fff'ffffU;
+  uint32_t x_e = x_abs >> FPBits::FRACTION_LEN;
+
+  // When |x| < 2^-26 (x_e <= 100) or |x| >= 32 (x_e >= 132):
+  if (LIBC_UNLIKELY(x_e - 101U >= 31U)) {
+    // |x| < 2^-26
+    if (x_e <= 100U) {
+#ifdef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
+      // 10^x rounds to 1 - 2^-24 when x <= log10(1 - 2^-25) ~ -0x1.bcb7b2p-27f.
+      return 0x3f80'0000U - static_cast<uint32_t>(x_u >= 0xb25e'5bd9U);
+#else  // !LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
+      if (x_abs == 0)
+        return 0x3f80'0000U;
+
+      if (rounding == FE_TONEAREST)
+        return 0x3f80'0000U - static_cast<uint32_t>(x_u >= 0xb25e'5bd9U);
+
+      if (rounding == FE_UPWARD && !is_neg)
+        return 0x3f80'0001U;
+
+      if ((rounding == FE_DOWNWARD || rounding == FE_TOWARDZERO) && is_neg)
+        return 0x3f7f'ffffU;
+
+      return 0x3f80'0000U;
+#endif // LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
+    }
+
+    // exp10(nan) = nan
+    if (x_abs > 0x7f80'0000U)
+      return x_u | 0x0040'0000U;
+
+    // x >= log10(2^128) or +inf
+    if (!is_neg && x_u >= 0x421a'209bU) {
+#ifndef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
+      if ((rounding == FE_DOWNWARD || rounding == FE_TOWARDZERO) &&
+          x_u < 0x7f80'0000U)
+        return 0x7f7f'ffffU;
+#endif // !LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
+      return 0x7f80'0000U;
+    }
+
+    // x < log10(2^-150) or -inf
+    if (x_u > 0xc234'9e35U) {
+#ifndef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
+      if (rounding == FE_UPWARD && x_u < 0xff80'0000U)
+        return 0x0000'0001U;
+#endif // !LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
+      return 0x0000'0000U;
+    }
+  }
+
+#ifndef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
+  // Exact outputs when x = 1, 2, ..., 10 in directed rounding modes.
+  // Quick check mask: 0x800f'ffffU = ~(bits of 1.0f | ... | bits of 10.0f)
+  if (rounding != FE_TONEAREST && LIBC_UNLIKELY((x_u & 0x800f'ffffU) == 0)) {
+    switch (x_u) {
+    case 0x3f80'0000U: // x = 1.0f
+      return 0x4120'0000U;
+    case 0x4000'0000U: // x = 2.0f
+      return 0x42c8'0000U;
+    case 0x4040'0000U: // x = 3.0f
+      return 0x447a'0000U;
+    case 0x4080'0000U: // x = 4.0f
+      return 0x461c'4000U;
+    case 0x40a0'0000U: // x = 5.0f
+      return 0x47c3'5000U;
+    case 0x40c0'0000U: // x = 6.0f
+      return 0x4974'2400U;
+    case 0x40e0'0000U: // x = 7.0f
+      return 0x4b18'9680U;
+    case 0x4100'0000U: // x = 8.0f
+      return 0x4cbe'bc20U;
+    case 0x4110'0000U: // x = 9.0f
+      return 0x4e6e'6b28U;
+    case 0x4120'0000U: // x = 10.0f
+      return 0x5015'02f9U;
+    }
+  }
+#endif // !LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
+
+  // Range reduction:
+  //   k = floor(x * log2(10))
+  //   x * log2(10) = k + u, with u in [0, 1)
+  //   10^x = 2^(k + u) = 2^k * 2^u
+  uint64_t u_bits = exp_b_scale(x_u, x_e, LOG2_10, 132U);
+  return exp2f_eval_bits(u_bits, is_neg, rounding);
+}
+
+// Statically rounded, no-except implementation of exp10f using integer-only
+// arithmetic.
+LIBC_ALWAYS_INLINE float exp10f(float x, [[maybe_unused]] int rounding) {
+  return cpp::bit_cast<float>(
+      exp10f_bits(cpp::bit_cast<uint32_t>(x), rounding));
+}
+
+} // namespace static_rounding
+
+namespace integer_eval {
+
+LIBC_ALWAYS_INLINE float exp10f(float x) {
+  return static_rounding::exp10f(x, FE_TONEAREST);
+}
+
+} // namespace integer_eval
+} // namespace math
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC___SUPPORT_MATH_EXP10F_INTEGER_EVAL_H
diff --git a/libc/src/__support/math/exp2f.h b/libc/src/__support/math/exp2f.h
index e7ce1ec29c03b..672b6a386871e 100644
--- a/libc/src/__support/math/exp2f.h
+++ b/libc/src/__support/math/exp2f.h
@@ -17,6 +17,7 @@
 #include "src/__support/common.h"
 #include "src/__support/macros/config.h"
 #include "src/__support/macros/optimization.h"
+#include "src/__support/macros/properties/cpu_features.h"
 
 #if defined(LIBC_MATH_HAS_SKIP_ACCURATE_PASS) &&                               \
     (defined(LIBC_MATH_HAS_SMALL_TABLES) ||                                    \
@@ -25,6 +26,13 @@
 #include "src/__support/math/exp2f_float_eval.h"
 #define LIBC_MATH_EXP2F_IMPL float_eval
 
+#elif !defined(LIBC_TARGET_CPU_HAS_FPU_DOUBLE) &&                              \
+    defined(LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY) &&                        \
+    defined(LIBC_MATH_HAS_NO_EXCEPT) && defined(LIBC_MATH_HAS_NO_ERRNO)
+
+#include "src/__support/math/exp2f_integer_eval.h"
+#define LIBC_MATH_EXP2F_IMPL integer_eval
+
 #else // !LIBC_MATH_HAS_SKIP_ACCURATE_PASS
 #include "src/__support/math/exp2f_double_eval.h"
 #define LIBC_MATH_EXP2F_IMPL double_eval
diff --git a/libc/src/__support/math/exp2f_integer_eval.h b/libc/src/__support/math/exp2f_integer_eval.h
new file mode 100644
index 0000000000000..11b557cdb6d07
--- /dev/null
+++ b/libc/src/__support/math/exp2f_integer_eval.h
@@ -0,0 +1,125 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Integer-only implementation of exp2f.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC___SUPPORT_MATH_EXP2F_INTEGER_EVAL_H
+#define LLVM_LIBC_SRC___SUPPORT_MATH_EXP2F_INTEGER_EVAL_H
+
+#include "hdr/fenv_macros.h"
+#include "src/__support/CPP/bit.h"
+#include "src/__support/FPUtil/FPBits.h"
+#include "src/__support/macros/attributes.h"
+#include "src/__support/macros/config.h"
+#include "src/__support/macros/optimization.h"
+#include "src/__support/macros/properties/types.h"
+#include "src/__support/math/exp2f_integer_utils.h"
+
+namespace LIBC_NAMESPACE_DECL {
+namespace math {
+namespace static_rounding {
+
+LIBC_ALWAYS_INLINE uint32_t exp2f_bits(uint32_t x_u,
+                                       [[maybe_unused]] int rounding) {
+  using FPBits = fputil::FPBits<float>;
+
+  bool is_neg = (x_u >> 31) != 0;
+  uint32_t x_abs = x_u & 0x7fff'ffffU;
+  uint32_t x_e = x_abs >> FPBits::FRACTION_LEN;
+
+  // When |x| < 2^-24 (x_e <= 102) or |x| >= 128 (x_e >= 134):
+  if (LIBC_UNLIKELY(x_e - 103U >= 31U)) {
+    // |x| < 2^-24
+    if (x_e <= 102U) {
+#ifdef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
+      // 2^x rounds to 1 - 2^-24 when x < log2(1 - 2^-25) ~ -0x1.715476p-25f.
+      return 0x3f80'0000U - static_cast<uint32_t>(x_u > 0xb338'aa3bU);
+#else  // !LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
+      if (x_abs == 0)
+        return 0x3f80'0000U;
+
+      if (rounding == FE_TONEAREST)
+        return 0x3f80'0000U - static_cast<uint32_t>(x_u > 0xb338'aa3bU);
+
+      if (rounding == FE_UPWARD && !is_neg)
+        return 0x3f80'0001U;
+
+      if ((rounding == FE_DOWNWARD || rounding == FE_TOWARDZERO) && is_neg)
+        return 0x3f7f'ffffU;
+
+      return 0x3f80'0000U;
+#endif // LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
+    }
+
+    // exp2(nan) = nan
+    if (x_abs > 0x7f80'0000U)
+      return x_u | 0x0040'0000U;
+
+    // x >= 128 or +inf
+    if (!is_neg) {
+#ifndef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
+      if ((rounding == FE_DOWNWARD || rounding == FE_TOWARDZERO) &&
+          x_u < 0x7f80'0000U)
+        return 0x7f7f'ffffU;
+#endif // !LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
+      return 0x7f80'0000U;
+    }
+
+    // x <= -150 or -inf
+    if (x_u >= 0xc316'0000U) {
+#ifndef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
+      if (rounding == FE_UPWARD && x_u < 0xff80'0000U)
+        return 0x0000'0001U;
+#endif // !LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
+      return 0x0000'0000U;
+    }
+  }
+
+  // Range reduction:
+  //   k = floor(x)
+  //   x = k + u, with u in [0, 1)
+  //   2^x = 2^(k + u) = 2^k * 2^u
+  //
+  // For all remaining inputs, x_e is in [103, 134], so shifting the 24-bit
+  // significand of |x| places the binary point of |x| at bit 54.
+#ifdef LIBC_TYPES_HAS_INT128
+  uint64_t m = (x_u & 0x007f'ffffU) | 0x0080'0000U;
+  uint64_t u_bits = m << (x_e - 96U);
+#else  // !LIBC_TYPES_HAS_INT128
+  uint32_t m_hi = ((x_u & 0x007f'ffffU) | 0x0080'0000U) << 6;
+  uint32_t s = 134U - x_e;
+  uint32_t u_lo = (m_hi << 1) << (31U - s);
+  uint32_t u_hi = m_hi >> s;
+  uint64_t u_bits = (static_cast<uint64_t>(u_hi) << 32) | u_lo;
+#endif // LIBC_TYPES_HAS_INT128
+
+  return exp2f_eval_bits(u_bits, is_neg, rounding);
+}
+
+// Statically rounded, no-except implementation of exp2f using integer-only
+// arithmetic.
+LIBC_ALWAYS_INLINE float exp2f(float x, [[maybe_unused]] int rounding) {
+  return cpp::bit_cast<float>(exp2f_bits(cpp::bit_cast<uint32_t>(x), rounding));
+}
+
+} // namespace static_rounding
+
+namespace integer_eval {
+
+LIBC_ALWAYS_INLINE float exp2f(float x) {
+  return static_rounding::exp2f(x, FE_TONEAREST);
+}
+
+} // namespace integer_eval
+} // namespace math
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC___SUPPORT_MATH_EXP2F_INTEGER_EVAL_H
diff --git a/libc/src/__support/math/exp2f_integer_utils.h b/libc/src/__support/math/exp2f_integer_utils.h
new file mode 100644
index 0000000000000..96e67c996a696
--- /dev/null
+++ b/libc/src/__support/math/exp2f_integer_utils.h
@@ -0,0 +1,163 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Shared utilities and constants for integer-only single-precision exponential
+/// functions.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC___SUPPORT_MATH_EXP2F_INTEGER_UTILS_H
+#define LLVM_LIBC_SRC___SUPPORT_MATH_EXP2F_INTEGER_UTILS_H
+
+#include "hdr/fenv_macros.h"
+#include "src/__support/FPUtil/PolyEval.h"
+#include "src/__support/FPUtil/multiply_add.h"
+#include "src/__support/frac64.h"
+#include "src/__support/macros/attributes.h"
+#include "src/__support/macros/config.h"
+#include "src/__support/macros/optimization.h"
+#include "src/__support/macros/properties/types.h"
+
+namespace LIBC_NAMESPACE_DECL {
+namespace math {
+namespace static_rounding {
+
+// Degree-11 minimax polynomial approximating 2^u - 1 on [0, 1], generated by
+// Sollya with:
+// > P = fpminimax(2^x, 11, [|1, 64...|], [0, 1], absolute, fixed);
+// > dirtyinfnorm(2^x - P(x), [0, 1]);
+//   0x1.6238...p-58
+// Store the fractional part of the coefficients below with LSB = 2^-64:
+LIBC_INLINE_VAR constexpr Frac64 EXP2F_COEFFS[11] = {
+    Frac64(0xb172'17f7'd1cf'b7cf), // u
+    Frac64(0x3d7f'7bff'057d'4a5e), // u^2
+    Frac64(0x0e35'846b'8363'9484), // u^3
+    Frac64(0x0276'556d'ec97'dcd4), // u^4
+    Frac64(0x0057'61ff'dc04'c7ff), // u^5
+    Frac64(0x000a'1847'b6e7'92ec), // u^6
+    Frac64(0x0000'ffe8'14e5'7033), // u^7
+    Frac64(0x0000'1628'b6e9'70c8), // u^8
+    Frac64(0x0000'01b8'8ce7'4088), // u^9
+    Frac64(0x0000'001c'18d5'cb29), // u^10
+    Frac64(0x0000'0002'b43f'4490), // u^11
+};
+
+// Compute |x| * log2(b) in unsigned 64-bit fixed-point format with binary
+// point at bit 54 (LSB = 2^-54), for x_e in [max_e - 31, max_e].
+// The binary point of log2_b is at bit (63 - (133 - max_e)).
+LIBC_ALWAYS_INLINE uint64_t exp_b_scale(uint32_t x_u, uint32_t x_e,
+                                        Frac64 log2_b, uint32_t max_e) {
+#ifdef LIBC_TYPES_HAS_INT128
+  uint64_t m = static_cast<uint64_t>((x_u & 0x007f'ffffU) | 0x0080'0000U)
+               << (x_e - (max_e - 38U));
+  return (Frac64(m) * log2_b).val[0];
+#else  // !LIBC_TYPES_HAS_INT128
+  // Place the 24-bit significand of |x| at bits [8..31] (leading 1 at bit 31)
+  // and multiply by log2_b_scaled using a 32x64 -> 64 high product.
+  // Since x_e is in [max_e - 31, max_e], s = max_e - x_e is in [0, 31], and
+  // shifting right by s places the binary point of |x| * log2(b) at bit 54.
+  uint64_t m = (x_u << 8) | 0x8000'0000U;
+  uint64_t log2_b_scaled = log2_b.val[0] >> 2;
+  uint64_t prod = m * (log2_b_scaled >> 32) +
+                  ((m * static_cast<uint32_t>(log2_b_scaled)) >> 32);
+  uint32_t s = max_e - x_e;
+  uint32_t prod_lo = static_cast<uint32_t>(prod);
+  uint32_t prod_hi = static_cast<uint32_t>(prod >> 32);
+  uint32_t u_lo = (prod_lo >> s) | ((prod_hi << 1) << (31U - s));
+  uint32_t u_hi = prod_hi >> s;
+  return (static_cast<uint64_t>(u_hi) << 32) | u_lo;
+#endif // LIBC_TYPES_HAS_INT128
+}
+
+// Evaluate 2^(x * log2(b)) from u_bits = |x| * log2(b) (with binary point at
+// bit 54) and return the IEEE 754 single-precision bit representation.
+LIBC_ALWAYS_INLINE uint32_t exp2f_eval_bits(uint64_t u_bits, bool is_neg,
+                                            [[maybe_unused]] int rounding) {
+  // Negate in two's complement when x < 0 so that floor(x * log2(b)) is the
+  // arithmetic right shift by 54, and the fractional part u in [0, 1) is the
+  // low 54 bits.
+  int64_t u_signed =
+      is_neg ? -static_cast<int64_t>(u_bits) : static_cast<int64_t>(u_bits);
+  int k =
+      (static_cast<int32_t>(static_cast<uint64_t>(u_signed) >> 32) >> 22) + 126;
+#ifdef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
+  Frac64 u((static_cast<uint64_t>(u_signed) << 10) | 1U);
+#else  // !LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
+  Frac64 u(static_cast<uint64_t>(u_signed) << 10);
+#endif // LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
+
+#ifdef LIBC_TYPES_HAS_INT128
+  // Evaluate p ~ 2^u - 1 on [0, 1) using 2-way Estrin/Horner scheme.
+  Frac64 u2 = u * u;
+  Frac64 p_even =
+      fputil::polyeval(u2, EXP2F_COEFFS[0], EXP2F_COEFFS[2], EXP2F_COEFFS[4],
+                       EXP2F_COEFFS[6], EXP2F_COEFFS[8], EXP2F_COEFFS[10]);
+  Frac64 p_odd =
+      fputil::polyeval(u2, EXP2F_COEFFS[1], EXP2F_COEFFS[3], EXP2F_COEFFS[5],
+                       EXP2F_COEFFS[7], EXP2F_COEFFS[9]);
+  Frac64 p = fputil::multiply_add(p_odd, u, p_even) * u;
+
+  if (LIBC_LIKELY(k >= 0)) {
+#ifdef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
+    return (((static_cast<uint32_t>(p.val[0] >> 40) + 1U) >> 1) +
+            (static_cast<uint32_t>(k + 1) << 23));
+#else  // !LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
+    if (LIBC_LIKELY(rounding == FE_TONEAREST))
+      return (((static_cast<uint32_t>(p.val[0] >> 40) + 1U) >> 1) +
+              (static_cast<uint32_t>(k + 1) << 23));
+#endif // LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
+  }
+#else  // !LIBC_TYPES_HAS_INT128
+  // Evaluate p ~ 2^u - 1 on [0, 1) using Horner's scheme.
+  Frac64 p(0);
+  LIBC_LOOP_NOUNROLL
+  for (int i = 10; i >= 0; --i)
+    p = (p + EXP2F_COEFFS[i]) * u;
+#endif // LIBC_TYPES_HAS_INT128
+
+  // Combine 2^u = 1 + p with 2^(k - 126):
+  // Place the explicit leading 1 of 2^u at bit 31 and the top 31 bits of p in
+  // bits [0..30].
+  // 1. Normal result (k >= 0, biased exponent k + 1 in [1, 254]):
+  //    Shift right by 7 to place the leading 1 at bit 24 (which after >> 1
+  //    contributes +1 to the biased exponent field, added to k << 23).
+  // 2. Subnormal result (k < 0, i.e., k in [-24, -1]):
+  //    Shift right by 7 - k in [8, 31] to place both the leading 1 and the
+  //    fractional bits directly at their subnormal bit positions.
+  uint32_t mant = static_cast<uint32_t>(p.val[0] >> 33) | 0x8000'0000U;
+  uint32_t shift = 7;
+  uint32_t exp_bits = static_cast<uint32_t>(k) << 23;
+
+  if (LIBC_UNLIKELY(k < 0)) {
+    shift = static_cast<uint32_t>(7 - k);
+    exp_bits = 0;
+  }
+
+#ifdef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
+  return (((mant >> shift) + 1U) >> 1) + exp_bits;
+#else  // !LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
+  if (rounding == FE_TONEAREST)
+    return (((mant >> shift) + 1U) >> 1) + exp_bits;
+
+  uint32_t round_up = 0;
+  if (LIBC_UNLIKELY(rounding == FE_UPWARD)) {
+    uint32_t mask = ~0U >> (31U - shift);
+    round_up = static_cast<uint32_t>(
+        ((mant & mask) | static_cast<uint32_t>(p.val[0])) != 0);
+  }
+
+  return ((mant >> shift) >> 1) + round_up + exp_bits;
+#endif // LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
+}
+
+} // namespace static_rounding
+} // namespace math
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC___SUPPORT_MATH_EXP2F_INTEGER_UTILS_H
diff --git a/libc/src/__support/math/expf_integer_eval.h b/libc/src/__support/math/expf_integer_eval.h
index 52736323d8d7e..fe708f70c2eca 100644
--- a/libc/src/__support/math/expf_integer_eval.h
+++ b/libc/src/__support/math/expf_integer_eval.h
@@ -7,8 +7,7 @@
 //===----------------------------------------------------------------------===//
 ///
 /// \file
-/// This file contains the statically rounded, integer-only implementation of
-/// expf(x)
+/// Integer-only implementation of expf.
 ///
 //===----------------------------------------------------------------------===//
 
@@ -18,278 +17,100 @@
 #include "hdr/fenv_macros.h"
 #include "src/__support/CPP/bit.h"
 #include "src/__support/FPUtil/FPBits.h"
-#include "src/__support/FPUtil/PolyEval.h"
 #include "src/__support/frac64.h"
+#include "src/__support/macros/attributes.h"
 #include "src/__support/macros/config.h"
 #include "src/__support/macros/optimization.h"
 #include "src/__support/math/check/exp_exceptions.h"
+#include "src/__support/math/exp2f_integer_utils.h"
 
 namespace LIBC_NAMESPACE_DECL {
-
-namespace shared {
-
 namespace math {
-
 namespace static_rounding {
 
-// print(2+round(1/log(2), 64, RN));
-// LSB(INV_LN2) = 2^-63
-LIBC_INLINE_VAR constexpr Frac64 INV_LN2 = Frac64(0xb8aa'3b29'5c17'f0bc);
-
-// 64-bit polynomial approximation of 2^x coefficients generated with Sollya:
-// > P = fpminimax(2^x, 11, [|1, 64...|], [0, 1], absolute, fixed);
-// Store the fractional part of the coefficients below
-// > dirtyinfnorm(2^x - P(x), [0, 1]);
-// 0x1.6238...p-58
-// LSB(EXPF_COEFFS[i]) = 2^-64
-LIBC_INLINE_VAR constexpr Frac64 EXPF_COEFFS[] = {
-    Frac64(0xb172'17f7'd1cf'b7cf), // x
-    Frac64(0x3d7f'7bff'057d'4a5e), // x^2
-    Frac64(0x0e35'846b'8363'9484), // x^3
-    Frac64(0x0276'556d'ec97'dcd4), // x^4
-    Frac64(0x0057'61ff'dc04'c7ff), // x^5
-    Frac64(0x000a'1847'b6e7'92ec), // x^6
-    Frac64(0x0000'ffe8'14e5'7033), // x^7
-    Frac64(0x0000'1628'b6e9'70c8), // x^8
-    Frac64(0x0000'01b8'8ce7'4088), // x^9
-    Frac64(0x0000'001c'18d5'cb29), // x^10
-    Frac64(0x0000'0002'b43f'4490), // x^11
-};
+// Constants generated by Sollya with:
+// > display = hexadecimal;
+// > round(log2(exp(1)) * 2^63, 64, RN);
+// LSB(LOG2_E) = 2^-63
+LIBC_INLINE_VAR constexpr Frac64 LOG2_E = Frac64(0xb8aa'3b29'5c17'f0bc);
 
-// Statically rounded, no except implementation of expf using integer-only
-// arithmetic.
-LIBC_INLINE float expf(float x, [[maybe_unused]] int rounding) {
+LIBC_ALWAYS_INLINE uint32_t expf_bits(uint32_t x_u,
+                                      [[maybe_unused]] int rounding) {
   using FPBits = fputil::FPBits<float>;
   using FPBounds = LIBC_NAMESPACE::math::check::exp_internal::Bounds<float>;
-  FPBits xbits(x);
 
-  bool is_neg = xbits.is_neg();
-  uint32_t x_val = xbits.uintval();
-  uint32_t x_val_abs = x_val & 0x7fff'ffffU;
+  bool is_neg = (x_u >> 31) != 0;
+  uint32_t x_abs = x_u & 0x7fff'ffffU;
+  uint32_t x_e = x_abs >> FPBits::FRACTION_LEN;
 
-  // When |x| >= smallest value that will cause overflow, |x| <= 2^-25, or x is
-  // NaN
-  if (LIBC_UNLIKELY(x_val_abs >= FPBounds::UPPER_BITS ||
-                    x_val_abs <= 0x3300'0000U)) {
-    // |x| <= 2^-25
-    if (x_val_abs <= 0x3300'0000U) {
+  // When |x| < 2^-25 (x_e <= 101) or |x| >= 64 (x_e >= 133):
+  if (LIBC_UNLIKELY(x_e - 102U >= 31U)) {
+    // |x| < 2^-25
+    if (x_e <= 101U) {
 #ifdef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
-      return 1.0f;
+      return 0x3f80'0000U;
 #else  // !LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
-      if (x_val_abs == 0)
-        return 1.0f;
+      if (x_abs == 0)
+        return 0x3f80'0000U;
 
       if (rounding == FE_UPWARD && !is_neg)
-        return 0x1.000002p0f;
+        return 0x3f80'0001U;
 
       if ((rounding == FE_DOWNWARD || rounding == FE_TOWARDZERO) && is_neg)
-        return 0x1.fffffep-1f;
+        return 0x3f7f'ffffU;
 
-      return 1.0f;
+      return 0x3f80'0000U;
 #endif // LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
     }
 
-    if (xbits.is_nan()) {
-      // Per conversation with lntue, we don't need to raise exception here,
-      // as we're assuming no FPUs/fenv in this kind of environment
-      if (xbits.is_signaling_nan()) {
-        // silencing
-        return FPBits::quiet_nan().get_val();
-      }
-
-      // quiet NaN
-      return x;
-    }
-
-    // e^-inf = 0
-    // e^+inf = +inf
-    if (xbits.is_inf()) {
-      return is_neg ? 0.0f : FPBits::inf().get_val();
-    }
+    // exp(nan) = nan
+    if (x_abs > 0x7f80'0000U)
+      return x_u | 0x0040'0000U;
 
-    // Large finite positive
-    if (!is_neg) {
+    // x >= 89 or +inf
+    if (!is_neg && x_u >= FPBounds::UPPER_BITS) {
 #ifndef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
-      if (rounding == FE_DOWNWARD || rounding == FE_TOWARDZERO)
-        return FPBits::max_normal().get_val();
-
-      return FPBits::inf().get_val();
-#else  // LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
-      return FPBits::inf().get_val();
+      if ((rounding == FE_DOWNWARD || rounding == FE_TOWARDZERO) &&
+          x_u < 0x7f80'0000U)
+        return 0x7f7f'ffffU;
 #endif // !LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
+      return 0x7f80'0000U;
     }
 
-    // x < log(2^-150) or NaN (NaN is already handled above)
-    if (xbits.uintval() >= 0xc2cf'f1b5U) {
+    // x < log(2^-150) or -inf
+    if (x_u >= 0xc2cf'f1b5U) {
 #ifndef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
-      if (rounding == FE_UPWARD)
-        return FPBits::min_subnormal().get_val();
+      if (rounding == FE_UPWARD && x_u < 0xff80'0000U)
+        return 0x0000'0001U;
 #endif // !LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
-
-      return 0.0f;
-    }
-  }
-
-  // Main calculations
-
-  uint16_t x_e = xbits.get_biased_exponent();
-  uint64_t x_u = xbits.get_mantissa();
-
-  // Range reduction
-  // The algorithm near the end of this function estimates 2^r,
-  // where r is the fractional part of x * log2(e) and is in [0, 1].
-  // See EXPF_COEFFS for more details on the approximation polynomial used.
-
-  // add leading bit = 1
-  x_u |= uint64_t(1) << FPBits::FRACTION_LEN;
-
-  // shift to top 32 bit --> decimal point at hidden bit that we've added
-  x_u <<= 32;
-
-  int x_e_unbiased = static_cast<int>(x_e) - FPBits::EXP_BIAS;
-
-  // shift for the decimal point to be at the hidden bit
-  if (x_e_unbiased > 0) {
-    x_u <<= x_e_unbiased;
-  } else if (x_e_unbiased < 0) {
-    x_u >>= -x_e_unbiased;
-  }
-
-  // LSB(x_u_frac) = 2^-55
-  Frac64 x_u_frac(x_u);
-
-  // LSB(x_ln2) = 2^-54
-  Frac64 x_ln2 = x_u_frac * INV_LN2;
-
-  constexpr uint64_t FRAC_MASK = (uint64_t(1) << 54) - 1;
-  uint64_t x_ln2_bit = x_ln2.val[0];
-
-  uint64_t e_y, l2y_r;
-  uint32_t e_y_unbiased;
-
-  // As Frac64 can't store the sign, we need to handle the sign separately:
-  // - Both branches are computing floor(x * log2(e)).
-  // - For negative x, we round up to the next multiple of 2^54, then clear the
-  // last 54 bits.
-  // - For positive x, we round down (just clear) the last 54 bits.
-  //
-  // Then, l2y_r is the remainder of x * log2(e) after removing the integer
-  // part, which is used to compute 2^l2y_r_frac - 1.
-  //
-  // e_y_unbiased is biased exponent field, but already bit-positioned to the
-  // exponent field of the float representation.
-  if (LIBC_UNLIKELY(is_neg)) {
-    e_y = (x_ln2_bit + FRAC_MASK) & ~FRAC_MASK;
-    l2y_r = e_y - x_ln2_bit;
-    e_y_unbiased = (FPBits::EXP_BIAS << 23) - static_cast<uint32_t>(e_y >> 31);
-  } else {
-    e_y = x_ln2_bit & ~FRAC_MASK;
-    l2y_r = x_ln2_bit - e_y;
-    e_y_unbiased = (FPBits::EXP_BIAS << 23) + static_cast<uint32_t>(e_y >> 31);
-  }
-
-  uint32_t k = static_cast<uint32_t>(e_y >> 54);
-  int d = static_cast<int>(k) - FPBits::EXP_BIAS;
-
-  // d >= 24 --> k >= 151
-  // --> guaranteed to below 2^-150
-  //
-  // underflow
-  if (LIBC_UNLIKELY(is_neg && d >= 24)) {
-    return 0.0f;
-  }
-
-  // LSB(l2y_r_frac) = LSB(l2y_r) * 2^-10 = 2^-64
-  Frac64 l2y_r_frac(l2y_r << 10);
-
-  // p = 2^l2y_r_frac - 1
-  Frac64 p = l2y_r_frac *
-             fputil::polyeval(l2y_r_frac, EXPF_COEFFS[0], EXPF_COEFFS[1],
-                              EXPF_COEFFS[2], EXPF_COEFFS[3], EXPF_COEFFS[4],
-                              EXPF_COEFFS[5], EXPF_COEFFS[6], EXPF_COEFFS[7],
-                              EXPF_COEFFS[8], EXPF_COEFFS[9], EXPF_COEFFS[10]);
-
-  uint32_t shift_length = 40;
-  uint32_t leading_one = 0;
-
-  // We're computing with errors < worst-cast errors, so tie-rounding never
-  // happens. Hence, round-to-nearest, tie-to-even is equivalent to
-  // round-to-nearest, tie-to-away. Which is what we're implementing below
-  // in the following order:
-  //
-  // 1. Shift so that the rounding bit is at bit-0
-  // 2. Add 1 for rounding
-  // 3. Perform another shift by 1
-  // 4. Depending on the rounding modes, adjust accoringly:
-  //  a. Add 1 if rounding-up (0 if not)
-  //  b. Add e_y_unbiased to the result (0 if the result is subnormal)
-
-  // subnormal
-  if (LIBC_UNLIKELY(is_neg && d >= 0)) {
-    e_y_unbiased = 0;
-    leading_one = 1 << (23 - d);
-
-    // In the below shifts, we're shifting by (shift_length + 1) at max, while
-    // shift_length is already 40, and if d = 23 --> shift_length + d = 63, and
-    // we'll shift by whole 64 bits, which is undefined behavior in C++.
-    //
-    // So, we'll truncate the last 2 bits.
-    if (d >= 22) {
-      d -= 2;
-      p.val[0] >>= 2;
+      return 0x0000'0000U;
     }
-
-    shift_length += d + 1;
   }
 
-#ifdef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
-  uint32_t result =
-      (static_cast<uint32_t>(p.val[0] >> shift_length) + (leading_one + 1));
-  result >>= 1;
-  result += e_y_unbiased;
-
-  return cpp::bit_cast<float>(result);
-#else  // !LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
-  if (rounding == FE_TONEAREST) {
-    uint32_t result =
-        (static_cast<uint32_t>(p.val[0] >> shift_length) + (leading_one + 1));
-    result >>= 1;
-    result += e_y_unbiased;
-
-    return cpp::bit_cast<float>(result);
-  }
-
-  uint32_t should_round_up = 0;
-
-  if (LIBC_UNLIKELY(rounding == FE_UPWARD)) {
-    uint64_t round_up_mask = (uint64_t(1) << (shift_length + 1)) - 1;
-    should_round_up = static_cast<uint32_t>((p.val[0] & round_up_mask) != 0);
-  }
-
-  uint32_t result = (static_cast<uint32_t>(p.val[0] >> (shift_length + 1)) +
-                     should_round_up + (leading_one >> 1));
-  result += e_y_unbiased;
+  // Range reduction:
+  //   k = floor(x * log2(e))
+  //   x * log2(e) = k + u, with u in [0, 1)
+  //   e^x = 2^(k + u) = 2^k * 2^u
+  uint64_t u_bits = exp_b_scale(x_u, x_e, LOG2_E, 133U);
+  return exp2f_eval_bits(u_bits, is_neg, rounding);
+}
 
-  return cpp::bit_cast<float>(result);
-#endif // LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
+// Statically rounded, no-except implementation of expf using integer-only
+// arithmetic.
+LIBC_ALWAYS_INLINE float expf(float x, [[maybe_unused]] int rounding) {
+  return cpp::bit_cast<float>(expf_bits(cpp::bit_cast<uint32_t>(x), rounding));
 }
 
 } // namespace static_rounding
 
-} // namespace math
-
-} // namespace shared
-
-namespace math {
 namespace integer_eval {
 
-LIBC_INLINE float expf(float x) {
-  return shared::math::static_rounding::expf(x, FE_TONEAREST);
+LIBC_ALWAYS_INLINE float expf(float x) {
+  return static_rounding::expf(x, FE_TONEAREST);
 }
 
 } // namespace integer_eval
 } // namespace math
-
 } // namespace LIBC_NAMESPACE_DECL
 
 #endif // LLVM_LIBC_SRC___SUPPORT_MATH_EXPF_INTEGER_EVAL_H
diff --git a/libc/test/shared/CMakeLists.txt b/libc/test/shared/CMakeLists.txt
index 74eda7cdaf065..9d46e7691d262 100644
--- a/libc/test/shared/CMakeLists.txt
+++ b/libc/test/shared/CMakeLists.txt
@@ -17,6 +17,7 @@ else()
     SRCS
       shared_math_test.cpp
     DEPENDS
+      libc.hdr.fenv_macros
       libc.src.__support.FPUtil.float128
       libc.src.__support.FPUtil.fp_bits
       libc.src.__support.math.acos
diff --git a/libc/test/shared/shared_math_test.cpp b/libc/test/shared/shared_math_test.cpp
index 7b785331f36c6..df265c3e6a05f 100644
--- a/libc/test/shared/shared_math_test.cpp
+++ b/libc/test/shared/shared_math_test.cpp
@@ -6,7 +6,9 @@
 //
 //===----------------------------------------------------------------------===//
 
+#include "hdr/fenv_macros.h"
 #include "shared/math.h"
+#include "shared/static_rounding_math.h"
 #include "src/__support/FPUtil/float128.h"
 #include "test/UnitTest/FPMatcher.h"
 #include "test/UnitTest/Test.h"
@@ -99,7 +101,7 @@ TEST(LlvmLibcSharedMathTest, AllFloat16) {
   EXPECT_FP_EQ(0.0f16, setpayloadsigf16_res);
   float16 neg_min_denormal = FPBits::min_subnormal(Sign::NEG).get_val();
   EXPECT_FP_EQ(neg_min_denormal, LIBC_NAMESPACE::shared::nextdownf16(0.0f16));
-  float16 min_denormal = FPBits::min_subnormal(Sign ::POS).get_val();
+  float16 min_denormal = FPBits::min_subnormal(Sign::POS).get_val();
   EXPECT_FP_EQ(min_denormal, LIBC_NAMESPACE::shared::nextupf16(0.0f16));
 
   EXPECT_FP_EQ(0.0f16, LIBC_NAMESPACE::shared::nextafterf16(0.0f16, 0.0f16));
@@ -255,7 +257,7 @@ TEST(LlvmLibcSharedMathTest, AllFloat) {
   EXPECT_FP_EQ(0.0f, setpayloadsigf_res);
   float neg_min_denormal = FPBits::min_subnormal(Sign::NEG).get_val();
   EXPECT_FP_EQ(neg_min_denormal, LIBC_NAMESPACE::shared::nextdownf(0.0f));
-  float min_denormal = FPBits::min_subnormal(Sign ::POS).get_val();
+  float min_denormal = FPBits::min_subnormal(Sign::POS).get_val();
   EXPECT_FP_EQ(min_denormal, LIBC_NAMESPACE::shared::nextupf(0.0f));
   EXPECT_FP_EQ(0.0f, LIBC_NAMESPACE::shared::nextafterf(0.0f, 0.0f));
 
@@ -390,7 +392,7 @@ TEST(LlvmLibcSharedMathTest, AllDouble) {
   EXPECT_FP_EQ(0.0, setpayloadsig_res);
   double neg_min_denormal = FPBits::min_subnormal(Sign::NEG).get_val();
   EXPECT_FP_EQ(neg_min_denormal, LIBC_NAMESPACE::shared::nextdown(0.0));
-  double min_denormal = FPBits::min_subnormal(Sign ::POS).get_val();
+  double min_denormal = FPBits::min_subnormal(Sign::POS).get_val();
   EXPECT_FP_EQ(min_denormal, LIBC_NAMESPACE::shared::nextup(0.0));
   EXPECT_FP_EQ(0.0, LIBC_NAMESPACE::shared::nextafter(0.0, 0.0));
 
@@ -511,7 +513,7 @@ TEST(LlvmLibcSharedMathTest, AllLongDouble) {
 
   long double neg_min_denormal = FPBits::min_subnormal(Sign::NEG).get_val();
   EXPECT_FP_EQ(neg_min_denormal, LIBC_NAMESPACE::shared::nextdownl(0.0L));
-  long double min_denormal = FPBits::min_subnormal(Sign ::POS).get_val();
+  long double min_denormal = FPBits::min_subnormal(Sign::POS).get_val();
   EXPECT_FP_EQ(min_denormal, LIBC_NAMESPACE::shared::nextupl(0.0L));
   EXPECT_FP_EQ(0.0L, LIBC_NAMESPACE::shared::nexttowardl(0.0L, 0.0L));
   EXPECT_FP_EQ(0.0L, LIBC_NAMESPACE::shared::nextafterl(0.0L, 0.0L));
@@ -698,7 +700,7 @@ TEST(LlvmLibcSharedMathTest, AllFloat128) {
   float128 neg_min_denormal = FPBits::min_subnormal(Sign::NEG).get_val();
   EXPECT_FP_EQ(neg_min_denormal,
                LIBC_NAMESPACE::shared::nextdownf128(float128(0.0)));
-  float128 min_denormal = FPBits::min_subnormal(Sign ::POS).get_val();
+  float128 min_denormal = FPBits::min_subnormal(Sign::POS).get_val();
   EXPECT_FP_EQ(min_denormal, LIBC_NAMESPACE::shared::nextupf128(float128(0.0)));
   EXPECT_FP_EQ(float128(0.0), LIBC_NAMESPACE::shared::nextafterf128(
                                   float128(0.0), float128(0.0)));
@@ -815,7 +817,7 @@ TEST(LlvmLibcSharedMathTest, AllBFloat16) {
   bfloat16 neg_min_denormal = FPBits::min_subnormal(Sign::NEG).get_val();
   EXPECT_FP_EQ(neg_min_denormal,
                LIBC_NAMESPACE::shared::nextdownbf16(bfloat16(0.0)));
-  bfloat16 min_denormal = FPBits::min_subnormal(Sign ::POS).get_val();
+  bfloat16 min_denormal = FPBits::min_subnormal(Sign::POS).get_val();
   EXPECT_FP_EQ(min_denormal, LIBC_NAMESPACE::shared::nextupbf16(bfloat16(0.0)));
 
   EXPECT_FP_EQ(bfloat16(0.0), LIBC_NAMESPACE::shared::nextafterbf16(
@@ -904,3 +906,92 @@ TEST(LlvmLibcSharedMathTest, AllBFloat16) {
   EXPECT_FP_EQ(bfloat16(0.0), LIBC_NAMESPACE::shared::tanbf16(bfloat16(0.0)));
   EXPECT_FP_EQ(bfloat16(0.0), LIBC_NAMESPACE::shared::truncbf16(bfloat16(0.0)));
 }
+
+TEST(LlvmLibcSharedMathTest, StaticRounding) {
+  using FPBits = LIBC_NAMESPACE::fputil::FPBits<float>;
+
+  EXPECT_FP_EQ(
+      1.0f, LIBC_NAMESPACE::shared::static_rounding::expf(0.0f, FE_TONEAREST));
+  EXPECT_FP_EQ(
+      1.0f, LIBC_NAMESPACE::shared::static_rounding::exp2f(0.0f, FE_TONEAREST));
+  EXPECT_FP_EQ(1.0f, LIBC_NAMESPACE::shared::static_rounding::exp10f(
+                         0.0f, FE_TONEAREST));
+
+  EXPECT_FP_EQ(
+      2.0f, LIBC_NAMESPACE::shared::static_rounding::exp2f(1.0f, FE_TONEAREST));
+  EXPECT_FP_EQ(0.5f, LIBC_NAMESPACE::shared::static_rounding::exp2f(
+                         -1.0f, FE_TONEAREST));
+  EXPECT_FP_EQ(10.0f, LIBC_NAMESPACE::shared::static_rounding::exp10f(
+                          1.0f, FE_TONEAREST));
+  EXPECT_FP_EQ(100.0f, LIBC_NAMESPACE::shared::static_rounding::exp10f(
+                           2.0f, FE_TONEAREST));
+
+  EXPECT_FP_EQ(0x1.5bf0a8p+1f, LIBC_NAMESPACE::shared::static_rounding::expf(
+                                   1.0f, FE_TONEAREST));
+  EXPECT_FP_EQ(0x1.5bf0aap+1f,
+               LIBC_NAMESPACE::shared::static_rounding::expf(1.0f, FE_UPWARD));
+  EXPECT_FP_EQ(0x1.5bf0a8p+1f, LIBC_NAMESPACE::shared::static_rounding::expf(
+                                   1.0f, FE_DOWNWARD));
+  EXPECT_FP_EQ(0x1.5bf0a8p+1f, LIBC_NAMESPACE::shared::static_rounding::expf(
+                                   1.0f, FE_TOWARDZERO));
+
+  EXPECT_FP_EQ(0x1.6a09e6p+0f, LIBC_NAMESPACE::shared::static_rounding::exp2f(
+                                   0.5f, FE_TONEAREST));
+  EXPECT_FP_EQ(0x1.6a09e8p+0f,
+               LIBC_NAMESPACE::shared::static_rounding::exp2f(0.5f, FE_UPWARD));
+  EXPECT_FP_EQ(0x1.6a09e6p+0f, LIBC_NAMESPACE::shared::static_rounding::exp2f(
+                                   0.5f, FE_DOWNWARD));
+  EXPECT_FP_EQ(0x1.6a09e6p+0f, LIBC_NAMESPACE::shared::static_rounding::exp2f(
+                                   0.5f, FE_TOWARDZERO));
+
+  EXPECT_FP_EQ(0x1.94c584p+1f, LIBC_NAMESPACE::shared::static_rounding::exp10f(
+                                   0.5f, FE_TONEAREST));
+  EXPECT_FP_EQ(0x1.94c584p+1f, LIBC_NAMESPACE::shared::static_rounding::exp10f(
+                                   0.5f, FE_UPWARD));
+  EXPECT_FP_EQ(0x1.94c582p+1f, LIBC_NAMESPACE::shared::static_rounding::exp10f(
+                                   0.5f, FE_DOWNWARD));
+  EXPECT_FP_EQ(0x1.94c582p+1f, LIBC_NAMESPACE::shared::static_rounding::exp10f(
+                                   0.5f, FE_TOWARDZERO));
+
+  int rounding_modes[] = {FE_TONEAREST, FE_UPWARD, FE_DOWNWARD, FE_TOWARDZERO};
+  for (int rm : rounding_modes) {
+    EXPECT_FP_EQ(1.0f, LIBC_NAMESPACE::shared::static_rounding::expf(0.0f, rm));
+    EXPECT_FP_EQ(1.0f,
+                 LIBC_NAMESPACE::shared::static_rounding::expf(-0.0f, rm));
+    EXPECT_FP_EQ(1.0f,
+                 LIBC_NAMESPACE::shared::static_rounding::exp2f(0.0f, rm));
+    EXPECT_FP_EQ(1.0f,
+                 LIBC_NAMESPACE::shared::static_rounding::exp2f(-0.0f, rm));
+    EXPECT_FP_EQ(1.0f,
+                 LIBC_NAMESPACE::shared::static_rounding::exp10f(0.0f, rm));
+    EXPECT_FP_EQ(1.0f,
+                 LIBC_NAMESPACE::shared::static_rounding::exp10f(-0.0f, rm));
+
+    EXPECT_TRUE(FPBits(LIBC_NAMESPACE::shared::static_rounding::expf(
+                           FPBits::quiet_nan().get_val(), rm))
+                    .is_nan());
+    EXPECT_TRUE(FPBits(LIBC_NAMESPACE::shared::static_rounding::exp2f(
+                           FPBits::quiet_nan().get_val(), rm))
+                    .is_nan());
+    EXPECT_TRUE(FPBits(LIBC_NAMESPACE::shared::static_rounding::exp10f(
+                           FPBits::quiet_nan().get_val(), rm))
+                    .is_nan());
+
+    EXPECT_FP_EQ(FPBits::inf(Sign::POS).get_val(),
+                 LIBC_NAMESPACE::shared::static_rounding::expf(
+                     FPBits::inf(Sign::POS).get_val(), rm));
+    EXPECT_FP_EQ(FPBits::inf(Sign::POS).get_val(),
+                 LIBC_NAMESPACE::shared::static_rounding::exp2f(
+                     FPBits::inf(Sign::POS).get_val(), rm));
+    EXPECT_FP_EQ(FPBits::inf(Sign::POS).get_val(),
+                 LIBC_NAMESPACE::shared::static_rounding::exp10f(
+                     FPBits::inf(Sign::POS).get_val(), rm));
+
+    EXPECT_FP_EQ(0.0f, LIBC_NAMESPACE::shared::static_rounding::expf(
+                           FPBits::inf(Sign::NEG).get_val(), rm));
+    EXPECT_FP_EQ(0.0f, LIBC_NAMESPACE::shared::static_rounding::exp2f(
+                           FPBits::inf(Sign::NEG).get_val(), rm));
+    EXPECT_FP_EQ(0.0f, LIBC_NAMESPACE::shared::static_rounding::exp10f(
+                           FPBits::inf(Sign::NEG).get_val(), rm));
+  }
+}
diff --git a/libc/test/src/__support/big_int_test.cpp b/libc/test/src/__support/big_int_test.cpp
index 93fa2317c3ee0..2640588bd0cb0 100644
--- a/libc/test/src/__support/big_int_test.cpp
+++ b/libc/test/src/__support/big_int_test.cpp
@@ -770,6 +770,7 @@ TEST(LlvmLibcUIntClassTest, FullMulTests) {
   } while (0)
 
 TEST(LlvmLibcUIntClassTest, QuickMulHiTests) {
+  TEST_QUICK_MUL_HI(64, 1);
   TEST_QUICK_MUL_HI(128, 1);
   TEST_QUICK_MUL_HI(192, 2);
   TEST_QUICK_MUL_HI(256, 3);
diff --git a/libc/test/src/math/CMakeLists.txt b/libc/test/src/math/CMakeLists.txt
index 470d4b9e4731a..7b05537390f66 100644
--- a/libc/test/src/math/CMakeLists.txt
+++ b/libc/test/src/math/CMakeLists.txt
@@ -1395,6 +1395,7 @@ add_fp_unittest(
     libc.src.math.exp2f
     libc.src.__support.math.exp2f_double_eval
     libc.src.__support.math.exp2f_float_eval
+    libc.src.__support.math.exp2f_integer_eval
     libc.src.__support.FPUtil.fp_bits
 )
 
@@ -1460,6 +1461,7 @@ add_fp_unittest(
     libc.src.math.exp10f
     libc.src.__support.math.exp10f_double_eval
     libc.src.__support.math.exp10f_float_eval
+    libc.src.__support.math.exp10f_integer_eval
     libc.src.__support.FPUtil.fp_bits
 )
 
diff --git a/libc/test/src/math/exhaustive/CMakeLists.txt b/libc/test/src/math/exhaustive/CMakeLists.txt
index e1a91fca9b4c8..38a3971429693 100644
--- a/libc/test/src/math/exhaustive/CMakeLists.txt
+++ b/libc/test/src/math/exhaustive/CMakeLists.txt
@@ -255,8 +255,11 @@ add_fp_unittest(
     exp2f_test.cpp
   DEPENDS
     .exhaustive_test
+    .exhaustive_test_static_rounding
     libc.src.math.exp2f
+    libc.src.__support.math.exp2f_double_eval
     libc.src.__support.math.exp2f_float_eval
+    libc.src.__support.math.exp2f_integer_eval
     libc.src.__support.FPUtil.fp_bits
   LINK_LIBRARIES
     -lpthread
@@ -287,8 +290,11 @@ add_fp_unittest(
     exp10f_test.cpp
   DEPENDS
     .exhaustive_test
+    .exhaustive_test_static_rounding
     libc.src.math.exp10f
+    libc.src.__support.math.exp10f_double_eval
     libc.src.__support.math.exp10f_float_eval
+    libc.src.__support.math.exp10f_integer_eval
     libc.src.__support.FPUtil.fp_bits
   LINK_LIBRARIES
     -lpthread
diff --git a/libc/test/src/math/exhaustive/exp10f_test.cpp b/libc/test/src/math/exhaustive/exp10f_test.cpp
index 150e3d0812b65..3cfc7b858a6a8 100644
--- a/libc/test/src/math/exhaustive/exp10f_test.cpp
+++ b/libc/test/src/math/exhaustive/exp10f_test.cpp
@@ -6,9 +6,12 @@
 //
 //===----------------------------------------------------------------------===//
 
+#include "src/__support/math/exp10f_double_eval.h"
 #include "src/__support/math/exp10f_float_eval.h"
+#include "src/__support/math/exp10f_integer_eval.h"
 #include "src/math/exp10f.h"
 #include "test/src/math/exhaustive/exhaustive_test.h"
+#include "test/src/math/exhaustive/exhaustive_test_static_rounding.h"
 #include "utils/MPFRWrapper/MPFRUtils.h"
 
 namespace mpfr = LIBC_NAMESPACE::testing::mpfr;
@@ -50,3 +53,18 @@ TEST_F(LlvmLibcExp10fFloatExhaustiveTest, PositiveRange) {
 TEST_F(LlvmLibcExp10fFloatExhaustiveTest, NegativeRange) {
   test_full_range(mpfr::RoundingMode::Nearest, NEG_START, NEG_STOP);
 }
+
+// Statically rounded implementation: tested against double_eval across all
+// roundings.
+using LlvmLibcExp10fStaticRoundingExhaustiveTest =
+    LlvmLibcStaticallyRoundedUnaryOpExhaustiveMathTest<
+        float, LIBC_NAMESPACE::math::double_eval::exp10f,
+        LIBC_NAMESPACE::math::static_rounding::exp10f>;
+
+TEST_F(LlvmLibcExp10fStaticRoundingExhaustiveTest, PositiveRange) {
+  test_full_range_all_roundings(POS_START, POS_STOP);
+}
+
+TEST_F(LlvmLibcExp10fStaticRoundingExhaustiveTest, NegativeRange) {
+  test_full_range_all_roundings(NEG_START, NEG_STOP);
+}
diff --git a/libc/test/src/math/exhaustive/exp2f_test.cpp b/libc/test/src/math/exhaustive/exp2f_test.cpp
index 33f94adb48455..79559d5bc2a52 100644
--- a/libc/test/src/math/exhaustive/exp2f_test.cpp
+++ b/libc/test/src/math/exhaustive/exp2f_test.cpp
@@ -6,9 +6,12 @@
 //
 //===----------------------------------------------------------------------===//
 
+#include "src/__support/math/exp2f_double_eval.h"
 #include "src/__support/math/exp2f_float_eval.h"
+#include "src/__support/math/exp2f_integer_eval.h"
 #include "src/math/exp2f.h"
 #include "test/src/math/exhaustive/exhaustive_test.h"
+#include "test/src/math/exhaustive/exhaustive_test_static_rounding.h"
 #include "utils/MPFRWrapper/MPFRUtils.h"
 
 namespace mpfr = LIBC_NAMESPACE::testing::mpfr;
@@ -50,3 +53,18 @@ TEST_F(LlvmLibcExp2fFloatExhaustiveTest, PositiveRange) {
 TEST_F(LlvmLibcExp2fFloatExhaustiveTest, NegativeRange) {
   test_full_range(mpfr::RoundingMode::Nearest, NEG_START, NEG_STOP);
 }
+
+// Statically rounded implementation: tested against double_eval across all
+// roundings.
+using LlvmLibcExp2fStaticRoundingExhaustiveTest =
+    LlvmLibcStaticallyRoundedUnaryOpExhaustiveMathTest<
+        float, LIBC_NAMESPACE::math::double_eval::exp2f,
+        LIBC_NAMESPACE::math::static_rounding::exp2f>;
+
+TEST_F(LlvmLibcExp2fStaticRoundingExhaustiveTest, PositiveRange) {
+  test_full_range_all_roundings(POS_START, POS_STOP);
+}
+
+TEST_F(LlvmLibcExp2fStaticRoundingExhaustiveTest, NegativeRange) {
+  test_full_range_all_roundings(NEG_START, NEG_STOP);
+}
diff --git a/libc/test/src/math/exhaustive/expf_test.cpp b/libc/test/src/math/exhaustive/expf_test.cpp
index 27aafd7d9ce7a..41027b48a0d15 100644
--- a/libc/test/src/math/exhaustive/expf_test.cpp
+++ b/libc/test/src/math/exhaustive/expf_test.cpp
@@ -59,7 +59,7 @@ TEST_F(LlvmLibcExpfFloatExhaustiveTest, NegativeRange) {
 using LlvmLibcExpfStaticRoundingExhaustiveTest =
     LlvmLibcStaticallyRoundedUnaryOpExhaustiveMathTest<
         float, LIBC_NAMESPACE::math::double_eval::expf,
-        LIBC_NAMESPACE::shared::math::static_rounding::expf>;
+        LIBC_NAMESPACE::math::static_rounding::expf>;
 
 TEST_F(LlvmLibcExpfStaticRoundingExhaustiveTest, PositiveRange) {
   test_full_range_all_roundings(POS_START, POS_STOP);
diff --git a/libc/test/src/math/exp10f_test.cpp b/libc/test/src/math/exp10f_test.cpp
index 763c61a1c66c9..74c51fe735364 100644
--- a/libc/test/src/math/exp10f_test.cpp
+++ b/libc/test/src/math/exp10f_test.cpp
@@ -20,6 +20,7 @@
 #include "src/__support/macros/optimization.h"
 #include "src/__support/math/exp10f_double_eval.h"
 #include "src/__support/math/exp10f_float_eval.h"
+#include "src/__support/math/exp10f_integer_eval.h"
 #include "src/math/exp10f.h"
 #include "test/UnitTest/FPMatcher.h"
 #include "test/UnitTest/Test.h"
@@ -29,21 +30,26 @@ namespace mpfr = LIBC_NAMESPACE::testing::mpfr;
 
 class Exp10fTest : public LIBC_NAMESPACE::testing::FPTest<float> {
 public:
-  void test_special_numbers(float (*func)(float)) {
+  void test_special_numbers(float (*func)(float), bool check_errno = true) {
     EXPECT_FP_EQ(aNaN, func(aNaN));
-    EXPECT_MATH_ERRNO(0);
+    if (check_errno)
+      EXPECT_MATH_ERRNO(0);
 
     EXPECT_FP_EQ(inf, func(inf));
-    EXPECT_MATH_ERRNO(0);
+    if (check_errno)
+      EXPECT_MATH_ERRNO(0);
 
     EXPECT_FP_EQ(0.0f, func(neg_inf));
-    EXPECT_MATH_ERRNO(0);
+    if (check_errno)
+      EXPECT_MATH_ERRNO(0);
 
     EXPECT_FP_EQ(1.0f, func(0.0f));
-    EXPECT_MATH_ERRNO(0);
+    if (check_errno)
+      EXPECT_MATH_ERRNO(0);
 
     EXPECT_FP_EQ(1.0f, func(-0.0f));
-    EXPECT_MATH_ERRNO(0);
+    if (check_errno)
+      EXPECT_MATH_ERRNO(0);
   }
 
   void test_overflow(float (*func)(float),
@@ -157,25 +163,44 @@ class Exp10fTest : public LIBC_NAMESPACE::testing::FPTest<float> {
   }
 };
 
-#define LIST_EXP10F_TESTS(suffix, func, ulp_tolerance, all_rounding)           \
+#define LIST_EXP10F_TESTS(suffix, func, ulp_tolerance, all_rounding,           \
+                          check_exception_and_errno, check_errno)              \
   using LlvmLibcExp10fTest##suffix = Exp10fTest;                               \
   TEST_F(LlvmLibcExp10fTest##suffix, SpecialNumbers) {                         \
-    test_special_numbers(&func);                                               \
+    test_special_numbers(&func, check_errno);                                  \
+  }                                                                            \
+  TEST_F(LlvmLibcExp10fTest##suffix, Overflow) {                               \
+    test_overflow(&func, check_exception_and_errno);                           \
   }                                                                            \
-  TEST_F(LlvmLibcExp10fTest##suffix, Overflow) { test_overflow(&func); }       \
   TEST_F(LlvmLibcExp10fTest##suffix, Underflow) {                              \
-    test_underflow(&func, ulp_tolerance, all_rounding);                        \
+    test_underflow(&func, ulp_tolerance, all_rounding,                         \
+                   check_exception_and_errno);                                 \
   }                                                                            \
   TEST_F(LlvmLibcExp10fTest##suffix, TrickyInputs) {                           \
     test_tricky_inputs(&func, ulp_tolerance, all_rounding);                    \
   }                                                                            \
   TEST_F(LlvmLibcExp10fTest##suffix, InFloatRange) {                           \
-    test_in_range(&func, ulp_tolerance, all_rounding);                         \
+    test_in_range(&func, ulp_tolerance, all_rounding, check_errno);            \
   }
 
 LIST_EXP10F_TESTS(Default, LIBC_NAMESPACE::exp10f, /*ulp_tolerance=*/0.5,
-                  /*all_rounding=*/true)
+                  /*all_rounding=*/true, /*check_exception_and_errno=*/true,
+                  /*check_errno=*/true)
 LIST_EXP10F_TESTS(DoubleEval, LIBC_NAMESPACE::math::double_eval::exp10f,
-                  /*ulp_tolerance=*/0.5, /*all_rounding=*/true)
+                  /*ulp_tolerance=*/0.5, /*all_rounding=*/true,
+                  /*check_exception_and_errno=*/true, /*check_errno=*/true)
 LIST_EXP10F_TESTS(FloatEval, LIBC_NAMESPACE::math::float_eval::exp10f,
-                  /*ulp_tolerance=*/1.5, /*all_rounding=*/false)
+                  /*ulp_tolerance=*/1.5, /*all_rounding=*/false,
+                  /*check_exception_and_errno=*/true, /*check_errno=*/true)
+LIST_EXP10F_TESTS(IntegerEval, LIBC_NAMESPACE::math::integer_eval::exp10f,
+                  /*ulp_tolerance=*/0.5, /*all_rounding=*/false,
+                  /*check_exception_and_errno=*/false, /*check_errno=*/false)
+
+static float exp10f_static_rounding(float x) {
+  return LIBC_NAMESPACE::math::static_rounding::exp10f(
+      x, LIBC_NAMESPACE::fputil::quick_get_round());
+}
+
+LIST_EXP10F_TESTS(StaticRounding, exp10f_static_rounding,
+                  /*ulp_tolerance=*/0.5, /*all_rounding=*/true,
+                  /*check_exception_and_errno=*/false, /*check_errno=*/false)
diff --git a/libc/test/src/math/exp2f_test.cpp b/libc/test/src/math/exp2f_test.cpp
index d5ed25b8aa092..47761afd932f0 100644
--- a/libc/test/src/math/exp2f_test.cpp
+++ b/libc/test/src/math/exp2f_test.cpp
@@ -20,6 +20,7 @@
 #include "src/__support/macros/optimization.h"
 #include "src/__support/math/exp2f_double_eval.h"
 #include "src/__support/math/exp2f_float_eval.h"
+#include "src/__support/math/exp2f_integer_eval.h"
 #include "src/math/exp2f.h"
 #include "test/UnitTest/FPMatcher.h"
 #include "test/UnitTest/Test.h"
@@ -35,21 +36,26 @@ namespace mpfr = LIBC_NAMESPACE::testing::mpfr;
 
 class Exp2fTest : public LIBC_NAMESPACE::testing::FPTest<float> {
 public:
-  void test_special_numbers(float (*func)(float)) {
+  void test_special_numbers(float (*func)(float), bool check_errno = true) {
     EXPECT_FP_EQ(aNaN, func(aNaN));
-    EXPECT_MATH_ERRNO(0);
+    if (check_errno)
+      EXPECT_MATH_ERRNO(0);
 
     EXPECT_FP_EQ(inf, func(inf));
-    EXPECT_MATH_ERRNO(0);
+    if (check_errno)
+      EXPECT_MATH_ERRNO(0);
 
     EXPECT_FP_EQ(0.0f, func(neg_inf));
-    EXPECT_MATH_ERRNO(0);
+    if (check_errno)
+      EXPECT_MATH_ERRNO(0);
 
     EXPECT_FP_EQ(1.0f, func(0.0f));
-    EXPECT_MATH_ERRNO(0);
+    if (check_errno)
+      EXPECT_MATH_ERRNO(0);
 
     EXPECT_FP_EQ(1.0f, func(-0.0f));
-    EXPECT_MATH_ERRNO(0);
+    if (check_errno)
+      EXPECT_MATH_ERRNO(0);
   }
 
   void test_overflow(float (*func)(float),
@@ -70,7 +76,7 @@ class Exp2fTest : public LIBC_NAMESPACE::testing::FPTest<float> {
   }
 
   void test_tricky_inputs(float (*func)(float), double ulp_tolerance = 0.5,
-                          bool all_rounding = true) {
+                          bool all_rounding = true, bool check_errno = true) {
     constexpr int N = 12;
     constexpr uint32_t INPUTS[N] = {
         0x3b429d37U, /*0x1.853a6ep-9f*/
@@ -95,7 +101,9 @@ class Exp2fTest : public LIBC_NAMESPACE::testing::FPTest<float> {
       } else {
         EXPECT_MPFR_MATCH(mpfr::Operation::Exp2, x, func(x), ulp_tolerance);
       }
-      EXPECT_MATH_ERRNO(0);
+      if (check_errno) {
+        EXPECT_MATH_ERRNO(0);
+      }
     }
   }
 
@@ -159,25 +167,44 @@ class Exp2fTest : public LIBC_NAMESPACE::testing::FPTest<float> {
   }
 };
 
-#define LIST_EXP2F_TESTS(suffix, func, ulp_tolerance, all_rounding)            \
+#define LIST_EXP2F_TESTS(suffix, func, ulp_tolerance, all_rounding,            \
+                         check_exception_and_errno, check_errno)               \
   using LlvmLibcExp2fTest##suffix = Exp2fTest;                                 \
   TEST_F(LlvmLibcExp2fTest##suffix, SpecialNumbers) {                          \
-    test_special_numbers(&func);                                               \
+    test_special_numbers(&func, check_errno);                                  \
+  }                                                                            \
+  TEST_F(LlvmLibcExp2fTest##suffix, Overflow) {                                \
+    test_overflow(&func, check_exception_and_errno);                           \
   }                                                                            \
-  TEST_F(LlvmLibcExp2fTest##suffix, Overflow) { test_overflow(&func); }        \
   TEST_F(LlvmLibcExp2fTest##suffix, TrickyInputs) {                            \
-    test_tricky_inputs(&func, ulp_tolerance, all_rounding);                    \
+    test_tricky_inputs(&func, ulp_tolerance, all_rounding, check_errno);       \
   }                                                                            \
   TEST_F(LlvmLibcExp2fTest##suffix, Underflow) {                               \
-    test_underflow(&func, ulp_tolerance, all_rounding);                        \
+    test_underflow(&func, ulp_tolerance, all_rounding,                         \
+                   check_exception_and_errno);                                 \
   }                                                                            \
   TEST_F(LlvmLibcExp2fTest##suffix, InFloatRange) {                            \
-    test_in_range(&func, ulp_tolerance, all_rounding);                         \
+    test_in_range(&func, ulp_tolerance, all_rounding, check_errno);            \
   }
 
 LIST_EXP2F_TESTS(Default, LIBC_NAMESPACE::exp2f,
-                 /*ulp_tolerance=*/TOLERANCE + 0.5, /*all_rounding=*/true)
+                 /*ulp_tolerance=*/TOLERANCE + 0.5, /*all_rounding=*/true,
+                 /*check_exception_and_errno=*/true, /*check_errno=*/true)
 LIST_EXP2F_TESTS(DoubleEval, LIBC_NAMESPACE::math::double_eval::exp2f,
-                 /*ulp_tolerance=*/TOLERANCE + 0.5, /*all_rounding=*/true)
+                 /*ulp_tolerance=*/TOLERANCE + 0.5, /*all_rounding=*/true,
+                 /*check_exception_and_errno=*/true, /*check_errno=*/true)
 LIST_EXP2F_TESTS(FloatEval, LIBC_NAMESPACE::math::float_eval::exp2f,
-                 /*ulp_tolerance=*/1.5, /*all_rounding=*/false)
+                 /*ulp_tolerance=*/1.5, /*all_rounding=*/false,
+                 /*check_exception_and_errno=*/true, /*check_errno=*/true)
+LIST_EXP2F_TESTS(IntegerEval, LIBC_NAMESPACE::math::integer_eval::exp2f,
+                 /*ulp_tolerance=*/0.5, /*all_rounding=*/false,
+                 /*check_exception_and_errno=*/false, /*check_errno=*/false)
+
+static float exp2f_static_rounding(float x) {
+  return LIBC_NAMESPACE::math::static_rounding::exp2f(
+      x, LIBC_NAMESPACE::fputil::quick_get_round());
+}
+
+LIST_EXP2F_TESTS(StaticRounding, exp2f_static_rounding,
+                 /*ulp_tolerance=*/0.5, /*all_rounding=*/true,
+                 /*check_exception_and_errno=*/false, /*check_errno=*/false)
diff --git a/libc/test/src/math/expf_test.cpp b/libc/test/src/math/expf_test.cpp
index 2cf30559adabb..67bbc0fba353f 100644
--- a/libc/test/src/math/expf_test.cpp
+++ b/libc/test/src/math/expf_test.cpp
@@ -185,7 +185,7 @@ LIST_EXPF_TESTS(IntegerEval, LIBC_NAMESPACE::math::integer_eval::expf,
                 /*check_exception_and_errno=*/false, /*check_errno=*/false)
 
 static float expf_static_rounding(float x) {
-  return LIBC_NAMESPACE::shared::math::static_rounding::expf(
+  return LIBC_NAMESPACE::math::static_rounding::expf(
       x, LIBC_NAMESPACE::fputil::quick_get_round());
 }
 
diff --git a/libc/test/src/math/smoke/CMakeLists.txt b/libc/test/src/math/smoke/CMakeLists.txt
index 0bfe73a2e643a..3d276be9e5afa 100644
--- a/libc/test/src/math/smoke/CMakeLists.txt
+++ b/libc/test/src/math/smoke/CMakeLists.txt
@@ -1496,6 +1496,7 @@ add_fp_unittest(
     libc.src.math.exp2f
     libc.src.__support.math.exp2f_double_eval
     libc.src.__support.math.exp2f_float_eval
+    libc.src.__support.math.exp2f_integer_eval
     libc.src.__support.FPUtil.fp_bits
 )
 
@@ -1560,6 +1561,7 @@ add_fp_unittest(
     libc.src.math.exp10f
     libc.src.__support.math.exp10f_double_eval
     libc.src.__support.math.exp10f_float_eval
+    libc.src.__support.math.exp10f_integer_eval
     libc.src.__support.FPUtil.fp_bits
 )
 
diff --git a/libc/test/src/math/smoke/exp10f_test.cpp b/libc/test/src/math/smoke/exp10f_test.cpp
index 00c65dcf6acb9..4d8560f095dae 100644
--- a/libc/test/src/math/smoke/exp10f_test.cpp
+++ b/libc/test/src/math/smoke/exp10f_test.cpp
@@ -18,6 +18,7 @@
 #include "src/__support/FPUtil/FPBits.h"
 #include "src/__support/math/exp10f_double_eval.h"
 #include "src/__support/math/exp10f_float_eval.h"
+#include "src/__support/math/exp10f_integer_eval.h"
 #include "src/math/exp10f.h"
 #include "test/UnitTest/FPMatcher.h"
 #include "test/UnitTest/Test.h"
@@ -25,28 +26,35 @@
 class Exp10fTest : public LIBC_NAMESPACE::testing::FPTest<float> {
 public:
   void test_special_numbers(float (*func)(float),
-                            bool check_snan_invalid = false) {
+                            bool check_snan_invalid = false,
+                            bool check_errno = true) {
     if (check_snan_invalid) {
       EXPECT_FP_EQ_WITH_EXCEPTION(aNaN, func(sNaN), FE_INVALID);
     } else {
       EXPECT_FP_EQ(aNaN, func(sNaN));
     }
-    EXPECT_MATH_ERRNO(0);
+    if (check_errno)
+      EXPECT_MATH_ERRNO(0);
 
     EXPECT_FP_EQ_ALL_ROUNDING(aNaN, func(aNaN));
-    EXPECT_MATH_ERRNO(0);
+    if (check_errno)
+      EXPECT_MATH_ERRNO(0);
 
     EXPECT_FP_EQ_ALL_ROUNDING(inf, func(inf));
-    EXPECT_MATH_ERRNO(0);
+    if (check_errno)
+      EXPECT_MATH_ERRNO(0);
 
     EXPECT_FP_EQ_ALL_ROUNDING(0.0f, func(neg_inf));
-    EXPECT_MATH_ERRNO(0);
+    if (check_errno)
+      EXPECT_MATH_ERRNO(0);
 
     EXPECT_FP_EQ_ALL_ROUNDING(1.0f, func(0.0f));
-    EXPECT_MATH_ERRNO(0);
+    if (check_errno)
+      EXPECT_MATH_ERRNO(0);
 
     EXPECT_FP_EQ_ALL_ROUNDING(1.0f, func(-0.0f));
-    EXPECT_MATH_ERRNO(0);
+    if (check_errno)
+      EXPECT_MATH_ERRNO(0);
 
     EXPECT_FP_EQ_ALL_ROUNDING(10.0f, func(1.0f));
     EXPECT_FP_EQ_ALL_ROUNDING(100.0f, func(2.0f));
@@ -97,16 +105,34 @@ class Exp10fTest : public LIBC_NAMESPACE::testing::FPTest<float> {
 #define LIST_EXP10F_FTZ_DAZ_TESTS(suffix, func)
 #endif // LIBC_TEST_FTZ_DAZ
 
-#define LIST_EXP10F_TESTS(suffix, func, check_snan_invalid)                    \
+#define LIST_EXP10F_TESTS(suffix, func, check_snan_invalid,                    \
+                          check_exception_and_errno, check_errno)              \
   using LlvmLibcExp10fTest##suffix = Exp10fTest;                               \
   TEST_F(LlvmLibcExp10fTest##suffix, SpecialNumbers) {                         \
-    test_special_numbers(&func, check_snan_invalid);                           \
+    test_special_numbers(&func, check_snan_invalid, check_errno);              \
+  }                                                                            \
+  TEST_F(LlvmLibcExp10fTest##suffix, Overflow) {                               \
+    test_overflow(&func, check_exception_and_errno);                           \
   }                                                                            \
-  TEST_F(LlvmLibcExp10fTest##suffix, Overflow) { test_overflow(&func); }       \
   LIST_EXP10F_FTZ_DAZ_TESTS(suffix, func)
 
-LIST_EXP10F_TESTS(Default, LIBC_NAMESPACE::exp10f, /*check_snan_invalid=*/true)
+LIST_EXP10F_TESTS(Default, LIBC_NAMESPACE::exp10f, /*check_snan_invalid=*/true,
+                  /*check_exception_and_errno=*/true, /*check_errno=*/true)
 LIST_EXP10F_TESTS(DoubleEval, LIBC_NAMESPACE::math::double_eval::exp10f,
-                  /*check_snan_invalid=*/false)
+                  /*check_snan_invalid=*/false,
+                  /*check_exception_and_errno=*/true, /*check_errno=*/true)
 LIST_EXP10F_TESTS(FloatEval, LIBC_NAMESPACE::math::float_eval::exp10f,
-                  /*check_snan_invalid=*/false)
+                  /*check_snan_invalid=*/false,
+                  /*check_exception_and_errno=*/true, /*check_errno=*/true)
+LIST_EXP10F_TESTS(IntegerEval, LIBC_NAMESPACE::math::integer_eval::exp10f,
+                  /*check_snan_invalid=*/false,
+                  /*check_exception_and_errno=*/false, /*check_errno=*/false)
+
+static float exp10f_static_rounding(float x) {
+  return LIBC_NAMESPACE::math::static_rounding::exp10f(
+      x, LIBC_NAMESPACE::fputil::quick_get_round());
+}
+
+LIST_EXP10F_TESTS(StaticRounding, exp10f_static_rounding,
+                  /*check_snan_invalid=*/false,
+                  /*check_exception_and_errno=*/false, /*check_errno=*/false)
diff --git a/libc/test/src/math/smoke/exp2f_test.cpp b/libc/test/src/math/smoke/exp2f_test.cpp
index bb64844eb7558..d3e79402c4920 100644
--- a/libc/test/src/math/smoke/exp2f_test.cpp
+++ b/libc/test/src/math/smoke/exp2f_test.cpp
@@ -18,6 +18,7 @@
 #include "src/__support/FPUtil/FPBits.h"
 #include "src/__support/math/exp2f_double_eval.h"
 #include "src/__support/math/exp2f_float_eval.h"
+#include "src/__support/math/exp2f_integer_eval.h"
 #include "src/math/exp2f.h"
 #include "test/UnitTest/FPMatcher.h"
 #include "test/UnitTest/Test.h"
@@ -25,28 +26,35 @@
 class Exp2fTest : public LIBC_NAMESPACE::testing::FPTest<float> {
 public:
   void test_special_numbers(float (*func)(float),
-                            bool check_snan_invalid = false) {
+                            bool check_snan_invalid = false,
+                            bool check_errno = true) {
     if (check_snan_invalid) {
       EXPECT_FP_EQ_WITH_EXCEPTION(aNaN, func(sNaN), FE_INVALID);
     } else {
       EXPECT_FP_EQ(aNaN, func(sNaN));
     }
-    EXPECT_MATH_ERRNO(0);
+    if (check_errno)
+      EXPECT_MATH_ERRNO(0);
 
     EXPECT_FP_EQ_ALL_ROUNDING(aNaN, func(aNaN));
-    EXPECT_MATH_ERRNO(0);
+    if (check_errno)
+      EXPECT_MATH_ERRNO(0);
 
     EXPECT_FP_EQ_ALL_ROUNDING(inf, func(inf));
-    EXPECT_MATH_ERRNO(0);
+    if (check_errno)
+      EXPECT_MATH_ERRNO(0);
 
     EXPECT_FP_EQ_ALL_ROUNDING(0.0f, func(neg_inf));
-    EXPECT_MATH_ERRNO(0);
+    if (check_errno)
+      EXPECT_MATH_ERRNO(0);
 
     EXPECT_FP_EQ_ALL_ROUNDING(1.0f, func(0.0f));
-    EXPECT_MATH_ERRNO(0);
+    if (check_errno)
+      EXPECT_MATH_ERRNO(0);
 
     EXPECT_FP_EQ_ALL_ROUNDING(1.0f, func(-0.0f));
-    EXPECT_MATH_ERRNO(0);
+    if (check_errno)
+      EXPECT_MATH_ERRNO(0);
 
     EXPECT_FP_EQ_ALL_ROUNDING(2.0f, func(1.0f));
     EXPECT_FP_EQ_ALL_ROUNDING(0.5f, func(-1.0f));
@@ -98,16 +106,34 @@ class Exp2fTest : public LIBC_NAMESPACE::testing::FPTest<float> {
 #define LIST_EXP2F_FTZ_DAZ_TESTS(suffix, func)
 #endif // LIBC_TEST_FTZ_DAZ
 
-#define LIST_EXP2F_TESTS(suffix, func, check_snan_invalid)                     \
+#define LIST_EXP2F_TESTS(suffix, func, check_snan_invalid,                     \
+                         check_exception_and_errno, check_errno)               \
   using LlvmLibcExp2fTest##suffix = Exp2fTest;                                 \
   TEST_F(LlvmLibcExp2fTest##suffix, SpecialNumbers) {                          \
-    test_special_numbers(&func, check_snan_invalid);                           \
+    test_special_numbers(&func, check_snan_invalid, check_errno);              \
+  }                                                                            \
+  TEST_F(LlvmLibcExp2fTest##suffix, Overflow) {                                \
+    test_overflow(&func, check_exception_and_errno);                           \
   }                                                                            \
-  TEST_F(LlvmLibcExp2fTest##suffix, Overflow) { test_overflow(&func); }        \
   LIST_EXP2F_FTZ_DAZ_TESTS(suffix, func)
 
-LIST_EXP2F_TESTS(Default, LIBC_NAMESPACE::exp2f, /*check_snan_invalid=*/true)
+LIST_EXP2F_TESTS(Default, LIBC_NAMESPACE::exp2f, /*check_snan_invalid=*/true,
+                 /*check_exception_and_errno=*/true, /*check_errno=*/true)
 LIST_EXP2F_TESTS(DoubleEval, LIBC_NAMESPACE::math::double_eval::exp2f,
-                 /*check_snan_invalid=*/false)
+                 /*check_snan_invalid=*/false,
+                 /*check_exception_and_errno=*/true, /*check_errno=*/true)
 LIST_EXP2F_TESTS(FloatEval, LIBC_NAMESPACE::math::float_eval::exp2f,
-                 /*check_snan_invalid=*/false)
+                 /*check_snan_invalid=*/false,
+                 /*check_exception_and_errno=*/true, /*check_errno=*/true)
+LIST_EXP2F_TESTS(IntegerEval, LIBC_NAMESPACE::math::integer_eval::exp2f,
+                 /*check_snan_invalid=*/false,
+                 /*check_exception_and_errno=*/false, /*check_errno=*/false)
+
+static float exp2f_static_rounding(float x) {
+  return LIBC_NAMESPACE::math::static_rounding::exp2f(
+      x, LIBC_NAMESPACE::fputil::quick_get_round());
+}
+
+LIST_EXP2F_TESTS(StaticRounding, exp2f_static_rounding,
+                 /*check_snan_invalid=*/false,
+                 /*check_exception_and_errno=*/false, /*check_errno=*/false)
diff --git a/libc/test/src/math/smoke/expf_test.cpp b/libc/test/src/math/smoke/expf_test.cpp
index f63d0d039fe41..bac9f9349de5e 100644
--- a/libc/test/src/math/smoke/expf_test.cpp
+++ b/libc/test/src/math/smoke/expf_test.cpp
@@ -125,7 +125,7 @@ LIST_EXPF_TESTS(IntegerEval, LIBC_NAMESPACE::math::integer_eval::expf,
                 /*check_exception_and_errno=*/false, /*check_errno=*/false)
 
 static float expf_static_rounding(float x) {
-  return LIBC_NAMESPACE::shared::math::static_rounding::expf(
+  return LIBC_NAMESPACE::math::static_rounding::expf(
       x, LIBC_NAMESPACE::fputil::quick_get_round());
 }
 
diff --git a/utils/bazel/llvm-project-overlay/libc/BUILD.bazel b/utils/bazel/llvm-project-overlay/libc/BUILD.bazel
index b6ec7c595fced..bfcc191a5fddb 100644
--- a/utils/bazel/llvm-project-overlay/libc/BUILD.bazel
+++ b/utils/bazel/llvm-project-overlay/libc/BUILD.bazel
@@ -8706,6 +8706,21 @@ libc_support_library(
     ],
 )
 
+libc_support_library(
+    name = "__support_math_exp2f_integer_utils",
+    hdrs = ["src/__support/math/exp2f_integer_utils.h"],
+    deps = [
+        ":__support_fputil_multiply_add",
+        ":__support_fputil_polyeval",
+        ":__support_frac64",
+        ":__support_macros_attributes",
+        ":__support_macros_config",
+        ":__support_macros_optimization",
+        ":__support_macros_properties_types",
+        ":hdr_fenv_macros",
+    ],
+)
+
 libc_support_library(
     name = "__support_math_expf_double_eval",
     hdrs = ["src/__support/math/expf_double_eval.h"],
@@ -8747,11 +8762,12 @@ libc_support_library(
     deps = [
         ":__support_cpp_bit",
         ":__support_fputil_fp_bits",
-        ":__support_fputil_polyeval",
         ":__support_frac64",
+        ":__support_macros_attributes",
         ":__support_macros_config",
         ":__support_macros_optimization",
         ":__support_math_check_exp_exceptions",
+        ":__support_math_exp2f_integer_utils",
         ":hdr_fenv_macros",
     ],
 )
@@ -10448,6 +10464,32 @@ libc_support_library(
     ],
 )
 
+libc_support_library(
+    name = "__support_math_exp2f_integer_eval",
+    hdrs = ["src/__support/math/exp2f_integer_eval.h"],
+    deps = [
+        ":__support_cpp_bit",
+        ":__support_fputil_fp_bits",
+        ":__support_macros_attributes",
+        ":__support_macros_config",
+        ":__support_macros_optimization",
+        ":__support_macros_properties_types",
+        ":__support_math_exp2f_integer_utils",
+        ":hdr_fenv_macros",
+    ],
+)
+
+libc_support_library(
+    name = "__support_math_static_rounding_exp2f",
+    hdrs = ["shared/math/static_rounding/exp2f.h"],
+    deps = [
+        ":__support_common",
+        ":__support_macros_config",
+        ":__support_math_exp2f_integer_eval",
+        ":shared_libc_common",
+    ],
+)
+
 libc_support_library(
     name = "__support_math_exp2f",
     hdrs = ["src/__support/math/exp2f.h"],
@@ -10455,8 +10497,10 @@ libc_support_library(
         ":__support_common",
         ":__support_macros_config",
         ":__support_macros_optimization",
+        ":__support_macros_properties_cpu_features",
         ":__support_math_exp2f_double_eval",
         ":__support_math_exp2f_float_eval",
+        ":__support_math_exp2f_integer_eval",
     ],
 )
 
@@ -10600,6 +10644,32 @@ libc_support_library(
     ],
 )
 
+libc_support_library(
+    name = "__support_math_exp10f_integer_eval",
+    hdrs = ["src/__support/math/exp10f_integer_eval.h"],
+    deps = [
+        ":__support_cpp_bit",
+        ":__support_fputil_fp_bits",
+        ":__support_frac64",
+        ":__support_macros_attributes",
+        ":__support_macros_config",
+        ":__support_macros_optimization",
+        ":__support_math_exp2f_integer_utils",
+        ":hdr_fenv_macros",
+    ],
+)
+
+libc_support_library(
+    name = "__support_math_static_rounding_exp10f",
+    hdrs = ["shared/math/static_rounding/exp10f.h"],
+    deps = [
+        ":__support_common",
+        ":__support_macros_config",
+        ":__support_math_exp10f_integer_eval",
+        ":shared_libc_common",
+    ],
+)
+
 libc_support_library(
     name = "__support_math_exp10f",
     hdrs = ["src/__support/math/exp10f.h"],
@@ -10607,8 +10677,10 @@ libc_support_library(
         ":__support_common",
         ":__support_macros_config",
         ":__support_macros_optimization",
+        ":__support_macros_properties_cpu_features",
         ":__support_math_exp10f_double_eval",
         ":__support_math_exp10f_float_eval",
+        ":__support_math_exp10f_integer_eval",
     ],
 )
 
@@ -12698,6 +12770,7 @@ libc_math_function(
     name = "exp10f",
     additional_deps = [
         ":__support_math_exp10f",
+        ":__support_math_static_rounding_exp10f",
         ":errno",
     ],
 )
@@ -12729,7 +12802,10 @@ libc_math_function(
 
 libc_math_function(
     name = "exp2f",
-    additional_deps = [":__support_math_exp2f"],
+    additional_deps = [
+        ":__support_math_exp2f",
+        ":__support_math_static_rounding_exp2f",
+    ],
 )
 
 libc_math_function(
diff --git a/utils/bazel/llvm-project-overlay/libc/test/src/math/BUILD.bazel b/utils/bazel/llvm-project-overlay/libc/test/src/math/BUILD.bazel
index 167d35ec0f269..d2cc8444362b7 100644
--- a/utils/bazel/llvm-project-overlay/libc/test/src/math/BUILD.bazel
+++ b/utils/bazel/llvm-project-overlay/libc/test/src/math/BUILD.bazel
@@ -181,6 +181,7 @@ math_mpfr_test(
     deps = [
         "//libc:__support_math_exp10f_double_eval",
         "//libc:__support_math_exp10f_float_eval",
+        "//libc:__support_math_exp10f_integer_eval",
     ],
 )
 
@@ -191,6 +192,7 @@ math_mpfr_test(
     deps = [
         "//libc:__support_math_exp2f_double_eval",
         "//libc:__support_math_exp2f_float_eval",
+        "//libc:__support_math_exp2f_integer_eval",
     ],
 )
 
diff --git a/utils/bazel/llvm-project-overlay/libc/test/src/math/smoke/BUILD.bazel b/utils/bazel/llvm-project-overlay/libc/test/src/math/smoke/BUILD.bazel
index c4a43c7f390e9..394eda0be654b 100644
--- a/utils/bazel/llvm-project-overlay/libc/test/src/math/smoke/BUILD.bazel
+++ b/utils/bazel/llvm-project-overlay/libc/test/src/math/smoke/BUILD.bazel
@@ -517,6 +517,7 @@ math_test(
     deps = [
         "//libc:__support_math_exp10f_double_eval",
         "//libc:__support_math_exp10f_float_eval",
+        "//libc:__support_math_exp10f_integer_eval",
     ],
 )
 
@@ -527,6 +528,7 @@ math_test(
     deps = [
         "//libc:__support_math_exp2f_double_eval",
         "//libc:__support_math_exp2f_float_eval",
+        "//libc:__support_math_exp2f_integer_eval",
     ],
 )
 



More information about the libc-commits mailing list