[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