[libc-commits] [libc] [libc][math] Add code size optimized float-only options for exp*f funtions. (PR #224735)
via libc-commits
libc-commits at lists.llvm.org
Sat Sep 19 06:14:35 PDT 2026
https://github.com/lntue updated https://github.com/llvm/llvm-project/pull/224735
>From c667643248c839e9b5900ec1ab7bacfdead623a9 Mon Sep 17 00:00:00 2001
From: Tue Ly <lntue.h at gmail.com>
Date: Fri, 18 Sep 2026 20:13:19 +0000
Subject: [PATCH 1/3] [libc][math] Add code size optimized float-only options
for exp*f functions.
---
libc/src/__support/math/CMakeLists.txt | 120 +++++++-
libc/src/__support/math/exp10f.h | 147 ++--------
libc/src/__support/math/exp10f_double_eval.h | 155 ++++++++++
libc/src/__support/math/exp10f_float_eval.h | 153 ++++++++++
libc/src/__support/math/exp2f.h | 171 ++----------
libc/src/__support/math/exp2f_double_eval.h | 178 ++++++++++++
libc/src/__support/math/exp2f_float_eval.h | 96 +++++++
libc/src/__support/math/exp2f_float_utils.h | 107 +++++++
libc/src/__support/math/expf.h | 122 ++------
libc/src/__support/math/expf_double_eval.h | 125 +++++++++
libc/src/__support/math/expf_float_eval.h | 126 +++++++++
libc/src/__support/math/expf_integer_eval.h | 19 +-
libc/src/math/generic/expf.cpp | 12 +-
libc/test/src/math/CMakeLists.txt | 24 +-
libc/test/src/math/exhaustive/CMakeLists.txt | 21 +-
.../src/math/exhaustive/exhaustive_test.h | 67 +++++
.../exhaustive_test_static_rounding.h | 7 +-
libc/test/src/math/exhaustive/exp10f_test.cpp | 37 ++-
libc/test/src/math/exhaustive/exp2f_test.cpp | 37 ++-
.../exhaustive/expf_static_rounding_test.cpp | 39 ---
libc/test/src/math/exhaustive/expf_test.cpp | 55 +++-
libc/test/src/math/exp10f_test.cpp | 264 +++++++++++-------
libc/test/src/math/exp2f_test.cpp | 254 ++++++++++-------
.../src/math/expf_static_rounding_test.cpp | 133 ---------
libc/test/src/math/expf_test.cpp | 250 +++++++++++------
libc/test/src/math/smoke/CMakeLists.txt | 22 +-
libc/test/src/math/smoke/exp10f_test.cpp | 158 ++++++-----
libc/test/src/math/smoke/exp2f_test.cpp | 160 ++++++-----
.../math/smoke/expf_static_rounding_test.cpp | 62 ----
libc/test/src/math/smoke/expf_test.cpp | 172 ++++++++----
30 files changed, 2122 insertions(+), 1171 deletions(-)
create mode 100644 libc/src/__support/math/exp10f_double_eval.h
create mode 100644 libc/src/__support/math/exp10f_float_eval.h
create mode 100644 libc/src/__support/math/exp2f_double_eval.h
create mode 100644 libc/src/__support/math/exp2f_float_eval.h
create mode 100644 libc/src/__support/math/exp2f_float_utils.h
create mode 100644 libc/src/__support/math/expf_double_eval.h
create mode 100644 libc/src/__support/math/expf_float_eval.h
delete mode 100644 libc/test/src/math/exhaustive/expf_static_rounding_test.cpp
delete mode 100644 libc/test/src/math/expf_static_rounding_test.cpp
delete mode 100644 libc/test/src/math/smoke/expf_static_rounding_test.cpp
diff --git a/libc/src/__support/math/CMakeLists.txt b/libc/src/__support/math/CMakeLists.txt
index 0871a5bf229b6..08017d264a300 100644
--- a/libc/src/__support/math/CMakeLists.txt
+++ b/libc/src/__support/math/CMakeLists.txt
@@ -3072,20 +3072,64 @@ add_header_library(
)
add_header_library(
- expf
+ exp2f_float_utils
HDRS
- expf.h
+ exp2f_float_utils.h
+ DEPENDS
+ libc.src.__support.CPP.bit
+ libc.src.__support.FPUtil.multiply_add
+ libc.src.__support.macros.config
+ libc.src.__support.macros.optimization
+)
+
+add_header_library(
+ expf_double_eval
+ HDRS
+ expf_double_eval.h
DEPENDS
.exp_float_constants
- libc.src.__support.common
libc.src.__support.FPUtil.fenv_impl
libc.src.__support.FPUtil.fp_bits
libc.src.__support.FPUtil.multiply_add
libc.src.__support.FPUtil.nearest_integer
libc.src.__support.FPUtil.polyeval
libc.src.__support.FPUtil.rounding_mode
+ libc.src.__support.common
+ libc.src.__support.macros.config
+ libc.src.__support.macros.optimization
+)
+
+add_header_library(
+ expf_float_eval
+ HDRS
+ expf_float_eval.h
+ DEPENDS
+ .exp2f_float_utils
+ libc.src.__support.FPUtil.double_double
+ libc.src.__support.FPUtil.fenv_impl
+ libc.src.__support.FPUtil.fp_bits
+ libc.src.__support.FPUtil.multiply_add
+ libc.src.__support.FPUtil.nearest_integer
+ libc.src.__support.FPUtil.rounding_mode
+ libc.src.__support.common
+ libc.src.__support.macros.config
+ libc.src.__support.macros.optimization
+ libc.src.__support.macros.properties.cpu_features
+)
+
+add_header_library(
+ expf
+ HDRS
+ expf.h
+ DEPENDS
+ .expf_double_eval
+ .expf_float_eval
+ .expf_integer_eval
+ libc.hdr.fenv_macros
+ libc.src.__support.common
libc.src.__support.macros.config
libc.src.__support.macros.optimization
+ libc.src.__support.macros.properties.cpu_features
)
add_header_library(
@@ -4178,9 +4222,9 @@ add_header_library(
)
add_header_library(
- exp2f
+ exp2f_double_eval
HDRS
- exp2f.h
+ exp2f_double_eval.h
DEPENDS
.exp10f_utils
libc.src.__support.FPUtil.except_value_utils
@@ -4190,9 +4234,38 @@ add_header_library(
libc.src.__support.FPUtil.nearest_integer
libc.src.__support.FPUtil.polyeval
libc.src.__support.FPUtil.rounding_mode
+ libc.src.__support.common
+ libc.src.__support.macros.config
+ libc.src.__support.macros.optimization
+ libc.src.__support.macros.properties.cpu_features
+)
+
+add_header_library(
+ exp2f_float_eval
+ HDRS
+ exp2f_float_eval.h
+ DEPENDS
+ .exp2f_float_utils
+ libc.src.__support.FPUtil.fenv_impl
+ libc.src.__support.FPUtil.fp_bits
+ libc.src.__support.FPUtil.multiply_add
+ libc.src.__support.FPUtil.nearest_integer
+ libc.src.__support.FPUtil.rounding_mode
+ libc.src.__support.common
+ libc.src.__support.macros.config
libc.src.__support.macros.optimization
+)
+
+add_header_library(
+ exp2f
+ HDRS
+ exp2f.h
+ DEPENDS
+ .exp2f_double_eval
+ .exp2f_float_eval
libc.src.__support.common
- libc.src.errno.errno
+ libc.src.__support.macros.config
+ libc.src.__support.macros.optimization
)
add_header_library(
@@ -4281,16 +4354,47 @@ add_header_library(
)
add_header_library(
- exp10f
+ exp10f_double_eval
HDRS
- exp10f.h
+ exp10f_double_eval.h
DEPENDS
.exp10f_utils
+ libc.src.__support.FPUtil.fenv_impl
+ libc.src.__support.FPUtil.fp_bits
+ libc.src.__support.FPUtil.multiply_add
+ libc.src.__support.FPUtil.nearest_integer
+ libc.src.__support.FPUtil.rounding_mode
libc.src.__support.macros.config
+ libc.src.__support.macros.optimization
+)
+
+add_header_library(
+ exp10f_float_eval
+ HDRS
+ exp10f_float_eval.h
+ DEPENDS
+ .exp2f_float_utils
+ libc.src.__support.FPUtil.double_double
libc.src.__support.FPUtil.fenv_impl
libc.src.__support.FPUtil.fp_bits
libc.src.__support.FPUtil.multiply_add
+ libc.src.__support.FPUtil.nearest_integer
libc.src.__support.FPUtil.rounding_mode
+ libc.src.__support.common
+ libc.src.__support.macros.config
+ libc.src.__support.macros.optimization
+ libc.src.__support.macros.properties.cpu_features
+)
+
+add_header_library(
+ exp10f
+ HDRS
+ exp10f.h
+ DEPENDS
+ .exp10f_double_eval
+ .exp10f_float_eval
+ libc.src.__support.common
+ libc.src.__support.macros.config
libc.src.__support.macros.optimization
)
diff --git a/libc/src/__support/math/exp10f.h b/libc/src/__support/math/exp10f.h
index 642370a3d2aa3..67a9d24a5a2c2 100644
--- a/libc/src/__support/math/exp10f.h
+++ b/libc/src/__support/math/exp10f.h
@@ -1,147 +1,44 @@
-//===-- Implementation header for exp10f ------------------------*- C++ -*-===//
+//===----------------------------------------------------------------------===//
//
// 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
+/// Implementation header for exp10f.
+///
+//===----------------------------------------------------------------------===//
#ifndef LLVM_LIBC_SRC___SUPPORT_MATH_EXP10F_H
#define LLVM_LIBC_SRC___SUPPORT_MATH_EXP10F_H
-#include "exp10f_utils.h"
-#include "src/__support/FPUtil/FEnvImpl.h"
-#include "src/__support/FPUtil/FPBits.h"
-#include "src/__support/FPUtil/multiply_add.h"
-#include "src/__support/FPUtil/rounding_mode.h"
+#include "src/__support/common.h"
#include "src/__support/macros/config.h"
-#include "src/__support/macros/optimization.h" // LIBC_UNLIKELY
-
-namespace LIBC_NAMESPACE_DECL {
-namespace math {
-
-LIBC_INLINE float exp10f(float x) {
- using FPBits = typename fputil::FPBits<float>;
- FPBits xbits(x);
+#include "src/__support/macros/optimization.h"
- uint32_t x_u = xbits.uintval();
- uint32_t x_abs = x_u & 0x7fff'ffffU;
+#if defined(LIBC_MATH_HAS_SKIP_ACCURATE_PASS) && \
+ (defined(LIBC_MATH_HAS_SMALL_TABLES) || \
+ defined(LIBC_MATH_HAS_INTERMEDIATE_COMP_IN_FLOAT))
- // When |x| >= log10(2^128), or x is nan
- if (LIBC_UNLIKELY(x_abs >= 0x421a'209bU)) {
- // When x < log10(2^-150) or nan
- if (x_u > 0xc234'9e35U) {
- // exp(-Inf) = 0
- if (xbits.is_inf())
- return 0.0f;
- // exp(nan) = nan
- if (xbits.is_nan())
- return x;
-#ifndef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
- if (fputil::fenv_is_round_up())
- return FPBits::min_subnormal().get_val();
-#endif
- fputil::set_errno_if_required(ERANGE);
- fputil::raise_except_if_required(FE_UNDERFLOW);
- return 0.0f;
- }
- // x >= log10(2^128) or nan
- if (xbits.is_pos() && (x_u >= 0x421a'209bU)) {
- // x is finite
- if (x_u < 0x7f80'0000U) {
-#ifndef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
- int rounding = fputil::quick_get_round();
- if (rounding == FE_DOWNWARD || rounding == FE_TOWARDZERO)
- return FPBits::max_normal().get_val();
-#endif
+#include "src/__support/math/exp10f_float_eval.h"
+#define LIBC_MATH_EXP10F_IMPL float_eval
- fputil::set_errno_if_required(ERANGE);
- fputil::raise_except_if_required(FE_OVERFLOW);
- }
- // x is +inf or nan
- return x + FPBits::inf().get_val();
- }
- }
+#else // !LIBC_MATH_HAS_SKIP_ACCURATE_PASS
+#include "src/__support/math/exp10f_double_eval.h"
+#define LIBC_MATH_EXP10F_IMPL double_eval
- // When |x| <= log10(2)*2^-6
- if (LIBC_UNLIKELY(x_abs <= 0x3b9a'209bU)) {
- if (LIBC_UNLIKELY(x_u == 0xb25e'5bd9U)) { // x = -0x1.bcb7b2p-27f
-#ifdef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
- return 0x1.fffffep-1f;
-#else
- if (fputil::fenv_is_round_to_nearest())
- return 0x1.fffffep-1f;
-#endif
- }
- // |x| < 2^-25
- // 10^x ~ 1 + log(10) * x
- if (LIBC_UNLIKELY(x_abs <= 0x3280'0000U)) {
- return fputil::multiply_add(x, 0x1.26bb1cp+1f, 1.0f);
- }
+#endif // LIBC_MATH_HAS_SKIP_ACCURATE_PASS
- return static_cast<float>(Exp10Base::powb_lo(x));
- }
-
- // Exceptional value.
- if (LIBC_UNLIKELY(x_u == 0x3d14'd956U)) { // x = 0x1.29b2acp-5f
-#ifndef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
- if (fputil::fenv_is_round_up())
- return 0x1.1657c4p+0f;
-#endif
- }
-
- // Exact outputs when x = 1, 2, ..., 10.
- // Quick check mask: 0x800f'ffffU = ~(bits of 1.0f | ... | bits of 10.0f)
- if (LIBC_UNLIKELY((x_u & 0x800f'ffffU) == 0)) {
- switch (x_u) {
- case 0x3f800000U: // x = 1.0f
- return 10.0f;
- case 0x40000000U: // x = 2.0f
- return 100.0f;
- case 0x40400000U: // x = 3.0f
- return 1'000.0f;
- case 0x40800000U: // x = 4.0f
- return 10'000.0f;
- case 0x40a00000U: // x = 5.0f
- return 100'000.0f;
- case 0x40c00000U: // x = 6.0f
- return 1'000'000.0f;
- case 0x40e00000U: // x = 7.0f
- return 10'000'000.0f;
- case 0x41000000U: // x = 8.0f
- return 100'000'000.0f;
- case 0x41100000U: // x = 9.0f
- return 1'000'000'000.0f;
- case 0x41200000U: // x = 10.0f
- return 10'000'000'000.0f;
- }
- }
-
- // Range reduction: 10^x = 2^(mid + hi) * 10^lo
- // rr = (2^(mid + hi), lo)
- auto rr = exp_b_range_reduc<Exp10Base>(x);
+namespace LIBC_NAMESPACE_DECL {
+namespace math {
- // The low part is approximated by a degree-5 minimax polynomial.
- // 10^lo ~ 1 + COEFFS[0] * lo + ... + COEFFS[4] * lo^5
- using fputil::multiply_add;
- double lo2 = rr.lo * rr.lo;
- // c0 = 1 + COEFFS[0] * lo
- double c0 = multiply_add(rr.lo, Exp10Base::COEFFS[0], 1.0);
- // c1 = COEFFS[1] + COEFFS[2] * lo
- double c1 = multiply_add(rr.lo, Exp10Base::COEFFS[2], Exp10Base::COEFFS[1]);
- // c2 = COEFFS[3] + COEFFS[4] * lo
- double c2 = multiply_add(rr.lo, Exp10Base::COEFFS[4], Exp10Base::COEFFS[3]);
- // p = c1 + c2 * lo^2
- // = COEFFS[1] + COEFFS[2] * lo + COEFFS[3] * lo^2 + COEFFS[4] * lo^3
- double p = multiply_add(lo2, c2, c1);
- // 10^lo ~ c0 + p * lo^2
- // 10^x = 2^(mid + hi) * 10^lo
- // ~ mh * (c0 + p * lo^2)
- // = (mh * c0) + p * (mh * lo^2)
- return static_cast<float>(multiply_add(p, lo2 * rr.mh, c0 * rr.mh));
-}
+using LIBC_MATH_EXP10F_IMPL::exp10f;
} // namespace math
} // namespace LIBC_NAMESPACE_DECL
+#undef LIBC_MATH_EXP10F_IMPL
+
#endif // LLVM_LIBC_SRC___SUPPORT_MATH_EXP10F_H
diff --git a/libc/src/__support/math/exp10f_double_eval.h b/libc/src/__support/math/exp10f_double_eval.h
new file mode 100644
index 0000000000000..03e3790c87e70
--- /dev/null
+++ b/libc/src/__support/math/exp10f_double_eval.h
@@ -0,0 +1,155 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+/// Double-precision implementation of exp10f.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC___SUPPORT_MATH_EXP10F_DOUBLE_EVAL_H
+#define LLVM_LIBC_SRC___SUPPORT_MATH_EXP10F_DOUBLE_EVAL_H
+
+#include "src/__support/FPUtil/FEnvImpl.h"
+#include "src/__support/FPUtil/FPBits.h"
+#include "src/__support/FPUtil/multiply_add.h"
+#include "src/__support/FPUtil/nearest_integer.h"
+#include "src/__support/FPUtil/rounding_mode.h"
+#include "src/__support/macros/config.h"
+#include "src/__support/macros/optimization.h"
+#include "src/__support/math/exp10f_utils.h"
+
+namespace LIBC_NAMESPACE_DECL {
+namespace math {
+namespace double_eval {
+
+LIBC_INLINE float exp10f(float x) {
+ using FPBits = typename fputil::FPBits<float>;
+ FPBits xbits(x);
+
+ uint32_t x_u = xbits.uintval();
+ uint32_t x_abs = x_u & 0x7fff'ffffU;
+
+ // When |x| >= log10(2^128), or x is nan
+ if (LIBC_UNLIKELY(x_abs >= 0x421a'209bU)) {
+ // When x < log10(2^-150) or nan
+ if (x_u > 0xc234'9e35U) {
+ // exp(-Inf) = 0
+ if (xbits.is_inf())
+ return 0.0f;
+ // exp(nan) = nan
+ if (xbits.is_nan())
+ return x;
+#ifndef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
+ if (fputil::fenv_is_round_up())
+ return FPBits::min_subnormal().get_val();
+#endif // LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
+ fputil::set_errno_if_required(ERANGE);
+ fputil::raise_except_if_required(FE_UNDERFLOW);
+ return 0.0f;
+ }
+ // x >= log10(2^128) or nan
+ if (xbits.is_pos() && (x_u >= 0x421a'209bU)) {
+ // x is finite
+ if (x_u < 0x7f80'0000U) {
+#ifndef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
+ int rounding = fputil::quick_get_round();
+ if (rounding == FE_DOWNWARD || rounding == FE_TOWARDZERO)
+ return FPBits::max_normal().get_val();
+#endif // LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
+
+ fputil::set_errno_if_required(ERANGE);
+ fputil::raise_except_if_required(FE_OVERFLOW);
+ }
+ // x is +inf or nan
+ return x + FPBits::inf().get_val();
+ }
+ }
+
+ // When |x| <= log10(2)*2^-6
+ if (LIBC_UNLIKELY(x_abs <= 0x3b9a'209bU)) {
+ if (LIBC_UNLIKELY(x_u == 0xb25e'5bd9U)) { // x = -0x1.bcb7b2p-27f
+#ifdef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
+ return 0x1.fffffep-1f;
+#else // !LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
+ if (fputil::fenv_is_round_to_nearest())
+ return 0x1.fffffep-1f;
+#endif // LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
+ }
+ // |x| < 2^-25
+ // 10^x ~ 1 + log(10) * x
+ if (LIBC_UNLIKELY(x_abs <= 0x3280'0000U)) {
+ return fputil::multiply_add(x, 0x1.26bb1cp+1f, 1.0f);
+ }
+
+ return static_cast<float>(Exp10Base::powb_lo(x));
+ }
+
+ // Exceptional value.
+ if (LIBC_UNLIKELY(x_u == 0x3d14'd956U)) { // x = 0x1.29b2acp-5f
+#ifndef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
+ if (fputil::fenv_is_round_up())
+ return 0x1.1657c4p+0f;
+#endif // LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
+ }
+
+ // Exact outputs when x = 1, 2, ..., 10.
+ // Quick check mask: 0x800f'ffffU = ~(bits of 1.0f | ... | bits of 10.0f)
+ if (LIBC_UNLIKELY((x_u & 0x800f'ffffU) == 0)) {
+ switch (x_u) {
+ case 0x3f800000U: // x = 1.0f
+ return 10.0f;
+ case 0x40000000U: // x = 2.0f
+ return 100.0f;
+ case 0x40400000U: // x = 3.0f
+ return 1'000.0f;
+ case 0x40800000U: // x = 4.0f
+ return 10'000.0f;
+ case 0x40a00000U: // x = 5.0f
+ return 100'000.0f;
+ case 0x40c00000U: // x = 6.0f
+ return 1'000'000.0f;
+ case 0x40e00000U: // x = 7.0f
+ return 10'000'000.0f;
+ case 0x41000000U: // x = 8.0f
+ return 100'000'000.0f;
+ case 0x41100000U: // x = 9.0f
+ return 1'000'000'000.0f;
+ case 0x41200000U: // x = 10.0f
+ return 10'000'000'000.0f;
+ }
+ }
+
+ // Range reduction: 10^x = 2^(mid + hi) * 10^lo
+ // rr = (2^(mid + hi), lo)
+ auto rr = exp_b_range_reduc<Exp10Base>(x);
+
+ // The low part is approximated by a degree-5 minimax polynomial.
+ // 10^lo ~ 1 + COEFFS[0] * lo + ... + COEFFS[4] * lo^5
+ using fputil::multiply_add;
+ double lo2 = rr.lo * rr.lo;
+ // c0 = 1 + COEFFS[0] * lo
+ double c0 = multiply_add(rr.lo, Exp10Base::COEFFS[0], 1.0);
+ // c1 = COEFFS[1] + COEFFS[2] * lo
+ double c1 = multiply_add(rr.lo, Exp10Base::COEFFS[2], Exp10Base::COEFFS[1]);
+ // c2 = COEFFS[3] + COEFFS[4] * lo
+ double c2 = multiply_add(rr.lo, Exp10Base::COEFFS[4], Exp10Base::COEFFS[3]);
+ // p = c1 + c2 * lo^2
+ // = COEFFS[1] + COEFFS[2] * lo + COEFFS[3] * lo^2 + COEFFS[4] * lo^3
+ double p = multiply_add(lo2, c2, c1);
+ // 10^lo ~ c0 + p * lo^2
+ // 10^x = 2^(mid + hi) * 10^lo
+ // ~ mh * (c0 + p * lo^2)
+ // = (mh * c0) + p * (mh * lo^2)
+ return static_cast<float>(multiply_add(p, lo2 * rr.mh, c0 * rr.mh));
+}
+
+} // namespace double_eval
+} // namespace math
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC___SUPPORT_MATH_EXP10F_DOUBLE_EVAL_H
diff --git a/libc/src/__support/math/exp10f_float_eval.h b/libc/src/__support/math/exp10f_float_eval.h
new file mode 100644
index 0000000000000..19bbfb93a509e
--- /dev/null
+++ b/libc/src/__support/math/exp10f_float_eval.h
@@ -0,0 +1,153 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+/// Float-only implementation of exp10f.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC___SUPPORT_MATH_EXP10F_FLOAT_EVAL_H
+#define LLVM_LIBC_SRC___SUPPORT_MATH_EXP10F_FLOAT_EVAL_H
+
+#include "src/__support/FPUtil/FEnvImpl.h"
+#include "src/__support/FPUtil/FPBits.h"
+#include "src/__support/FPUtil/double_double.h"
+#include "src/__support/FPUtil/multiply_add.h"
+#include "src/__support/FPUtil/nearest_integer.h"
+#include "src/__support/FPUtil/rounding_mode.h"
+#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"
+#include "src/__support/math/exp2f_float_utils.h"
+
+namespace LIBC_NAMESPACE_DECL {
+namespace math {
+namespace float_eval {
+
+LIBC_INLINE float exp10f(float x) {
+ using FPBits = typename fputil::FPBits<float>;
+ FPBits xbits(x);
+
+ uint32_t x_u = xbits.uintval();
+ uint32_t x_abs = x_u & 0x7fff'ffffU;
+
+ // When |x| >= log10(2^128), or x is nan
+ if (LIBC_UNLIKELY(x_abs >= 0x421a'209bU)) {
+ // When x < log10(2^-150) or nan
+ if (x_u > 0xc234'9e35U) {
+ // exp(-Inf) = 0
+ if (xbits.is_inf())
+ return 0.0f;
+ // exp(nan) = nan
+ if (xbits.is_nan())
+ return x;
+#ifndef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
+ if (fputil::fenv_is_round_up())
+ return FPBits::min_subnormal().get_val();
+#endif // LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
+ fputil::set_errno_if_required(ERANGE);
+ fputil::raise_except_if_required(FE_UNDERFLOW);
+ return 0.0f;
+ }
+ // x >= log10(2^128) or nan
+ if (xbits.is_pos() && (x_u >= 0x421a'209bU)) {
+ // x is finite
+ if (x_u < 0x7f80'0000U) {
+#ifndef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
+ int rounding = fputil::quick_get_round();
+ if (rounding == FE_DOWNWARD || rounding == FE_TOWARDZERO)
+ return FPBits::max_normal().get_val();
+#endif // LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
+
+ fputil::set_errno_if_required(ERANGE);
+ fputil::raise_except_if_required(FE_OVERFLOW);
+ }
+ // x is +inf or nan
+ return x + FPBits::inf().get_val();
+ }
+ }
+
+ // |x| <= 2^-25
+ // 10^x ~ 1 + log(10) * x
+ if (LIBC_UNLIKELY(x_abs <= 0x3280'0000U)) {
+ return fputil::multiply_add(x, 0x1.26bb1cp+1f, 1.0f);
+ }
+
+ // Exact outputs when x = 1, 2, ..., 10.
+ // Quick check mask: 0x800f'ffffU = ~(bits of 1.0f | ... | bits of 10.0f)
+ if (LIBC_UNLIKELY((x_u & 0x800f'ffffU) == 0)) {
+ switch (x_u) {
+ case 0x3f800000U: // x = 1.0f
+ return 10.0f;
+ case 0x40000000U: // x = 2.0f
+ return 100.0f;
+ case 0x40400000U: // x = 3.0f
+ return 1'000.0f;
+ case 0x40800000U: // x = 4.0f
+ return 10'000.0f;
+ case 0x40a00000U: // x = 5.0f
+ return 100'000.0f;
+ case 0x40c00000U: // x = 6.0f
+ return 1'000'000.0f;
+ case 0x40e00000U: // x = 7.0f
+ return 10'000'000.0f;
+ case 0x41000000U: // x = 8.0f
+ return 100'000'000.0f;
+ case 0x41100000U: // x = 9.0f
+ return 1'000'000'000.0f;
+ case 0x41200000U: // x = 10.0f
+ return 10'000'000'000.0f;
+ }
+ }
+
+ // Range reduction:
+ // k = round(x * log2(10))
+ // x * log2(10) = k + u, with |u| <= 0.5
+ // 10^x = 2^(k + u) = 2^k * 2^u
+#if defined(LIBC_TARGET_CPU_HAS_FMA_FLOAT)
+ // Constants generated by Sollya with:
+ // > display = hexadecimal;
+ // > hi = round(log2(10), SG, RN);
+ // > lo = round(log2(10) - hi, SG, RN);
+ constexpr fputil::FloatFloat LOG2_10 = {0x1.2f346ep-24f, 0x1.a934fp+1f};
+
+ float kf = fputil::nearest_integer(x * LOG2_10.hi);
+ int k = static_cast<int>(kf);
+
+ float u_hi = fputil::multiply_add(x, LOG2_10.hi, -kf);
+ float u = fputil::multiply_add(x, LOG2_10.lo, u_hi);
+#else // !LIBC_TARGET_CPU_HAS_FMA_FLOAT
+ // Cody-Waite reduction for non-FMA targets:
+ // Constants generated by Sollya with:
+ // > display = hexadecimal;
+ // > LOG2_10 = round(log2(10), SG, RN);
+ // > LOG10_2_HI = round(log10(2), 12, RN);
+ // > LOG10_2_LO = round(log10(2) - LOG10_2_HI, SG, RN);
+ constexpr float LOG2_10 = 0x1.a934fp+1f;
+ constexpr float LOG10_2_HI = 0x1.344p-2f;
+ constexpr float LOG10_2_LO = 0x1.3509f8p-18f;
+
+ float kf = fputil::nearest_integer(x * LOG2_10);
+ int k = static_cast<int>(kf);
+
+ float v_hi = fputil::multiply_add(-kf, LOG10_2_HI, x);
+ float v = fputil::multiply_add(-kf, LOG10_2_LO, v_hi);
+
+ // Convert reduced argument to base-2: u = v * log2(10)
+ float u = v * LOG2_10;
+#endif // LIBC_TARGET_CPU_HAS_FMA_FLOAT
+
+ return exp2f_eval(u, k);
+}
+
+} // namespace float_eval
+} // namespace math
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC___SUPPORT_MATH_EXP10F_FLOAT_EVAL_H
diff --git a/libc/src/__support/math/exp2f.h b/libc/src/__support/math/exp2f.h
index 1fa24c2013ede..e7ce1ec29c03b 100644
--- a/libc/src/__support/math/exp2f.h
+++ b/libc/src/__support/math/exp2f.h
@@ -1,173 +1,44 @@
-//===-- Implementation header for exp2f -------------------------*- C++ -*-===//
+//===----------------------------------------------------------------------===//
//
// 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
+/// Implementation header for exp2f.
+///
+//===----------------------------------------------------------------------===//
#ifndef LLVM_LIBC_SRC___SUPPORT_MATH_EXP2F_H
#define LLVM_LIBC_SRC___SUPPORT_MATH_EXP2F_H
-#include "exp10f_utils.h"
-#include "src/__support/FPUtil/FEnvImpl.h"
-#include "src/__support/FPUtil/FPBits.h"
-#include "src/__support/FPUtil/PolyEval.h"
-#include "src/__support/FPUtil/except_value_utils.h"
-#include "src/__support/FPUtil/multiply_add.h"
-#include "src/__support/FPUtil/nearest_integer.h"
-#include "src/__support/FPUtil/rounding_mode.h"
#include "src/__support/common.h"
#include "src/__support/macros/config.h"
-#include "src/__support/macros/optimization.h" // LIBC_UNLIKELY
-#include "src/__support/macros/properties/cpu_features.h"
-
-namespace LIBC_NAMESPACE_DECL {
-
-namespace math {
-
-LIBC_INLINE float exp2f(float x) {
- using FPBits = typename fputil::FPBits<float>;
- FPBits xbits(x);
-
- uint32_t x_u = xbits.uintval();
- uint32_t x_abs = x_u & 0x7fff'ffffU;
-
- // When |x| >= 128, or x is nan, or |x| <= 2^-5
- if (LIBC_UNLIKELY(x_abs >= 0x4300'0000U || x_abs <= 0x3d00'0000U)) {
- // |x| <= 2^-5
- if (x_abs <= 0x3d00'0000) {
- // |x| < 2^-25
- if (LIBC_UNLIKELY(x_abs <= 0x3280'0000U)) {
- return 1.0f + x;
- }
+#include "src/__support/macros/optimization.h"
-#ifndef LIBC_MATH_HAS_SKIP_ACCURATE_PASS
- constexpr uint32_t EXVAL1 = 0x3b42'9d37U;
- constexpr uint32_t EXVAL2 = 0xbcf3'a937U;
- constexpr uint32_t EXVAL_MASK = EXVAL1 & EXVAL2;
+#if defined(LIBC_MATH_HAS_SKIP_ACCURATE_PASS) && \
+ (defined(LIBC_MATH_HAS_SMALL_TABLES) || \
+ defined(LIBC_MATH_HAS_INTERMEDIATE_COMP_IN_FLOAT))
- // Check exceptional values.
- if (LIBC_UNLIKELY((x_u & EXVAL_MASK) == EXVAL_MASK)) {
- if (LIBC_UNLIKELY(x_u == EXVAL1)) { // x = 0x1.853a6ep-9f
- return fputil::round_result_slightly_down(0x1.00870ap+0f);
- } else if (LIBC_UNLIKELY(x_u == EXVAL2)) { // x = -0x1.e7526ep-6f
- return fputil::round_result_slightly_down(0x1.f58d62p-1f);
- }
- }
-#endif // !LIBC_MATH_HAS_SKIP_ACCURATE_PASS
+#include "src/__support/math/exp2f_float_eval.h"
+#define LIBC_MATH_EXP2F_IMPL float_eval
- // Minimax polynomial generated by Sollya with:
- // > P = fpminimax((2^x - 1)/x, 5, [|D...|], [-2^-5, 2^-5]);
- constexpr double COEFFS[] = {
- 0x1.62e42fefa39f3p-1, 0x1.ebfbdff82c57bp-3, 0x1.c6b08d6f2d7aap-5,
- 0x1.3b2ab6fc92f5dp-7, 0x1.5d897cfe27125p-10, 0x1.43090e61e6af1p-13};
- double xd = static_cast<double>(x);
- double xsq = xd * xd;
- double c0 = fputil::multiply_add(xd, COEFFS[1], COEFFS[0]);
- double c1 = fputil::multiply_add(xd, COEFFS[3], COEFFS[2]);
- double c2 = fputil::multiply_add(xd, COEFFS[5], COEFFS[4]);
- double p = fputil::polyeval(xsq, c0, c1, c2);
- double r = fputil::multiply_add(p, xd, 1.0);
- return static_cast<float>(r);
- }
+#else // !LIBC_MATH_HAS_SKIP_ACCURATE_PASS
+#include "src/__support/math/exp2f_double_eval.h"
+#define LIBC_MATH_EXP2F_IMPL double_eval
- // x >= 128
- if (xbits.is_pos()) {
- // x is finite
- if (x_u < 0x7f80'0000U) {
-#ifndef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
- int rounding = fputil::quick_get_round();
- if (rounding == FE_DOWNWARD || rounding == FE_TOWARDZERO)
- return FPBits::max_normal().get_val();
-#endif
+#endif // LIBC_MATH_HAS_SKIP_ACCURATE_PASS
- fputil::set_errno_if_required(ERANGE);
- fputil::raise_except_if_required(FE_OVERFLOW);
- }
- // x is +inf or nan
- return x + FPBits::inf().get_val();
- }
- // x <= -150
- if (x_u >= 0xc316'0000U) {
- // exp(-Inf) = 0
- if (xbits.is_inf())
- return 0.0f;
- // exp(nan) = nan
- if (xbits.is_nan())
- return x;
-#ifndef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
- if (fputil::fenv_is_round_up())
- return FPBits::min_subnormal().get_val();
-#endif
- if (x != 0.0f) {
- fputil::set_errno_if_required(ERANGE);
- fputil::raise_except_if_required(FE_UNDERFLOW);
- }
- return 0.0f;
- }
- }
-
- // For -150 < x < 128, to compute 2^x, we perform the following range
- // reduction: find hi, mid, lo such that:
- // x = hi + mid + lo, in which
- // hi is an integer,
- // 0 <= mid * 2^5 < 32 is an integer
- // -2^(-6) <= lo <= 2^-6.
- // In particular,
- // hi + mid = round(x * 2^5) * 2^(-5).
- // Then,
- // 2^x = 2^(hi + mid + lo) = 2^hi * 2^mid * 2^lo.
- // 2^mid is stored in the lookup table of 32 elements.
- // 2^lo is computed using a degree-5 minimax polynomial
- // generated by Sollya.
- // We perform 2^hi * 2^mid by simply add hi to the exponent field
- // of 2^mid.
-
- // kf = (hi + mid) * 2^5 = round(x * 2^5)
- float kf = 0;
- int k = 0;
-#ifdef LIBC_TARGET_CPU_HAS_NEAREST_INT
- kf = fputil::nearest_integer(x * 32.0f);
- k = static_cast<int>(kf);
-#else
- constexpr float HALF[2] = {0.5f, -0.5f};
- k = static_cast<int>(fputil::multiply_add(x, 32.0f, HALF[x < 0.0f]));
- kf = static_cast<float>(k);
-#endif // LIBC_TARGET_CPU_HAS_NEAREST_INT
-
- // dx = lo = x - (hi + mid) = x - kf * 2^(-5)
- double dx = fputil::multiply_add(-0x1.0p-5f, kf, x);
-
- // hi = floor(kf * 2^(-4))
- // exp_hi = shift hi to the exponent field of double precision.
- int64_t exp_hi =
- static_cast<int64_t>(static_cast<uint64_t>(k >> ExpBase::MID_BITS)
- << fputil::FPBits<double>::FRACTION_LEN);
- // mh = 2^hi * 2^mid
- // mh_bits = bit field of mh
- int64_t mh_bits = ExpBase::EXP_2_MID[k & ExpBase::MID_MASK] + exp_hi;
- double mh = fputil::FPBits<double>(uint64_t(mh_bits)).get_val();
+namespace LIBC_NAMESPACE_DECL {
+namespace math {
- // Degree-5 polynomial approximating (2^x - 1)/x generating by Sollya with:
- // > P = fpminimax((2^x - 1)/x, 5, [|D...|], [-1/32. 1/32]);
- constexpr double COEFFS[5] = {0x1.62e42fefa39efp-1, 0x1.ebfbdff8131c4p-3,
- 0x1.c6b08d7061695p-5, 0x1.3b2b1bee74b2ap-7,
- 0x1.5d88091198529p-10};
- double dx_sq = dx * dx;
- double c1 = fputil::multiply_add(dx, COEFFS[0], 1.0);
- double c2 = fputil::multiply_add(dx, COEFFS[2], COEFFS[1]);
- double c3 = fputil::multiply_add(dx, COEFFS[4], COEFFS[3]);
- double p = fputil::multiply_add(dx_sq, c3, c2);
- // 2^x = 2^(hi + mid + lo)
- // = 2^(hi + mid) * 2^lo
- // ~ mh * (1 + lo * P(lo))
- // = mh + (mh*lo) * P(lo)
- return static_cast<float>(fputil::multiply_add(p, dx_sq * mh, c1 * mh));
-}
+using LIBC_MATH_EXP2F_IMPL::exp2f;
} // namespace math
-
} // namespace LIBC_NAMESPACE_DECL
+#undef LIBC_MATH_EXP2F_IMPL
+
#endif // LLVM_LIBC_SRC___SUPPORT_MATH_EXP2F_H
diff --git a/libc/src/__support/math/exp2f_double_eval.h b/libc/src/__support/math/exp2f_double_eval.h
new file mode 100644
index 0000000000000..821e726d012d3
--- /dev/null
+++ b/libc/src/__support/math/exp2f_double_eval.h
@@ -0,0 +1,178 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+/// Double-precision implementation of exp2f.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC___SUPPORT_MATH_EXP2F_DOUBLE_EVAL_H
+#define LLVM_LIBC_SRC___SUPPORT_MATH_EXP2F_DOUBLE_EVAL_H
+
+#include "src/__support/FPUtil/FEnvImpl.h"
+#include "src/__support/FPUtil/FPBits.h"
+#include "src/__support/FPUtil/PolyEval.h"
+#include "src/__support/FPUtil/except_value_utils.h"
+#include "src/__support/FPUtil/multiply_add.h"
+#include "src/__support/FPUtil/nearest_integer.h"
+#include "src/__support/FPUtil/rounding_mode.h"
+#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"
+#include "src/__support/math/exp10f_utils.h"
+
+namespace LIBC_NAMESPACE_DECL {
+namespace math {
+namespace double_eval {
+
+LIBC_INLINE float exp2f(float x) {
+ using FPBits = typename fputil::FPBits<float>;
+ FPBits xbits(x);
+
+ uint32_t x_u = xbits.uintval();
+ uint32_t x_abs = x_u & 0x7fff'ffffU;
+
+ // When |x| >= 128, or x is nan, or |x| <= 2^-5
+ if (LIBC_UNLIKELY(x_abs >= 0x4300'0000U || x_abs <= 0x3d00'0000U)) {
+ // |x| <= 2^-5
+ if (x_abs <= 0x3d00'0000) {
+ // |x| < 2^-25
+ if (LIBC_UNLIKELY(x_abs <= 0x3280'0000U)) {
+ return 1.0f + x;
+ }
+
+#ifndef LIBC_MATH_HAS_SKIP_ACCURATE_PASS
+ constexpr uint32_t EXVAL1 = 0x3b42'9d37U;
+ constexpr uint32_t EXVAL2 = 0xbcf3'a937U;
+ constexpr uint32_t EXVAL_MASK = EXVAL1 & EXVAL2;
+
+ // Check exceptional values.
+ if (LIBC_UNLIKELY((x_u & EXVAL_MASK) == EXVAL_MASK)) {
+ if (LIBC_UNLIKELY(x_u == EXVAL1)) { // x = 0x1.853a6ep-9f
+ return fputil::round_result_slightly_down(0x1.00870ap+0f);
+ } else if (LIBC_UNLIKELY(x_u == EXVAL2)) { // x = -0x1.e7526ep-6f
+ return fputil::round_result_slightly_down(0x1.f58d62p-1f);
+ }
+ }
+#endif // !LIBC_MATH_HAS_SKIP_ACCURATE_PASS
+
+ // Minimax polynomial generated by Sollya with:
+ // > P = fpminimax((2^x - 1)/x, 5, [|D...|], [-2^-5, 2^-5]);
+ constexpr double COEFFS[] = {
+ 0x1.62e42fefa39f3p-1, 0x1.ebfbdff82c57bp-3, 0x1.c6b08d6f2d7aap-5,
+ 0x1.3b2ab6fc92f5dp-7, 0x1.5d897cfe27125p-10, 0x1.43090e61e6af1p-13};
+ double xd = static_cast<double>(x);
+ double xsq = xd * xd;
+ double c0 = fputil::multiply_add(xd, COEFFS[1], COEFFS[0]);
+ double c1 = fputil::multiply_add(xd, COEFFS[3], COEFFS[2]);
+ double c2 = fputil::multiply_add(xd, COEFFS[5], COEFFS[4]);
+ double p = fputil::polyeval(xsq, c0, c1, c2);
+ double r = fputil::multiply_add(p, xd, 1.0);
+ return static_cast<float>(r);
+ }
+
+ // x >= 128
+ if (xbits.is_pos()) {
+ // x is finite
+ if (x_u < 0x7f80'0000U) {
+#ifndef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
+ int rounding = fputil::quick_get_round();
+ if (rounding == FE_DOWNWARD || rounding == FE_TOWARDZERO)
+ return FPBits::max_normal().get_val();
+#endif // LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
+
+ fputil::set_errno_if_required(ERANGE);
+ fputil::raise_except_if_required(FE_OVERFLOW);
+ }
+ // x is +inf or nan
+ return x + FPBits::inf().get_val();
+ }
+ // x <= -150
+ if (x_u >= 0xc316'0000U) {
+ // exp(-Inf) = 0
+ if (xbits.is_inf())
+ return 0.0f;
+ // exp(nan) = nan
+ if (xbits.is_nan())
+ return x;
+#ifndef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
+ if (fputil::fenv_is_round_up())
+ return FPBits::min_subnormal().get_val();
+#endif // LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
+ if (x != 0.0f) {
+ fputil::set_errno_if_required(ERANGE);
+ fputil::raise_except_if_required(FE_UNDERFLOW);
+ }
+ return 0.0f;
+ }
+ }
+
+ // For -150 < x < 128, to compute 2^x, we perform the following range
+ // reduction: find hi, mid, lo such that:
+ // x = hi + mid + lo, in which
+ // hi is an integer,
+ // 0 <= mid * 2^5 < 32 is an integer
+ // -2^(-6) <= lo <= 2^-6.
+ // In particular,
+ // hi + mid = round(x * 2^5) * 2^(-5).
+ // Then,
+ // 2^x = 2^(hi + mid + lo) = 2^hi * 2^mid * 2^lo.
+ // 2^mid is stored in the lookup table of 32 elements.
+ // 2^lo is computed using a degree-5 minimax polynomial
+ // generated by Sollya.
+ // We perform 2^hi * 2^mid by simply add hi to the exponent field
+ // of 2^mid.
+
+ // kf = (hi + mid) * 2^5 = round(x * 2^5)
+ float kf = 0;
+ int k = 0;
+#ifdef LIBC_TARGET_CPU_HAS_NEAREST_INT
+ kf = fputil::nearest_integer(x * 32.0f);
+ k = static_cast<int>(kf);
+#else // !LIBC_TARGET_CPU_HAS_NEAREST_INT
+ constexpr float HALF[2] = {0.5f, -0.5f};
+ k = static_cast<int>(fputil::multiply_add(x, 32.0f, HALF[x < 0.0f]));
+ kf = static_cast<float>(k);
+#endif // LIBC_TARGET_CPU_HAS_NEAREST_INT
+
+ // dx = lo = x - (hi + mid) = x - kf * 2^(-5)
+ double dx = fputil::multiply_add(-0x1.0p-5f, kf, x);
+
+ // hi = floor(kf * 2^(-4))
+ // exp_hi = shift hi to the exponent field of double precision.
+ int64_t exp_hi =
+ static_cast<int64_t>(static_cast<uint64_t>(k >> ExpBase::MID_BITS)
+ << fputil::FPBits<double>::FRACTION_LEN);
+ // mh = 2^hi * 2^mid
+ // mh_bits = bit field of mh
+ int64_t mh_bits = ExpBase::EXP_2_MID[k & ExpBase::MID_MASK] + exp_hi;
+ double mh = fputil::FPBits<double>(uint64_t(mh_bits)).get_val();
+
+ // Degree-5 polynomial approximating (2^x - 1)/x generating by Sollya with:
+ // > P = fpminimax((2^x - 1)/x, 5, [|D...|], [-1/32. 1/32]);
+ constexpr double COEFFS[5] = {0x1.62e42fefa39efp-1, 0x1.ebfbdff8131c4p-3,
+ 0x1.c6b08d7061695p-5, 0x1.3b2b1bee74b2ap-7,
+ 0x1.5d88091198529p-10};
+ double dx_sq = dx * dx;
+ double c1 = fputil::multiply_add(dx, COEFFS[0], 1.0);
+ double c2 = fputil::multiply_add(dx, COEFFS[2], COEFFS[1]);
+ double c3 = fputil::multiply_add(dx, COEFFS[4], COEFFS[3]);
+ double p = fputil::multiply_add(dx_sq, c3, c2);
+ // 2^x = 2^(hi + mid + lo)
+ // = 2^(hi + mid) * 2^lo
+ // ~ mh * (1 + lo * P(lo))
+ // = mh + (mh*lo) * P(lo)
+ return static_cast<float>(fputil::multiply_add(p, dx_sq * mh, c1 * mh));
+}
+
+} // namespace double_eval
+} // namespace math
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC___SUPPORT_MATH_EXP2F_DOUBLE_EVAL_H
diff --git a/libc/src/__support/math/exp2f_float_eval.h b/libc/src/__support/math/exp2f_float_eval.h
new file mode 100644
index 0000000000000..8298ae7b18add
--- /dev/null
+++ b/libc/src/__support/math/exp2f_float_eval.h
@@ -0,0 +1,96 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+/// Float-only implementation of exp2f.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC___SUPPORT_MATH_EXP2F_FLOAT_EVAL_H
+#define LLVM_LIBC_SRC___SUPPORT_MATH_EXP2F_FLOAT_EVAL_H
+
+#include "src/__support/FPUtil/FEnvImpl.h"
+#include "src/__support/FPUtil/FPBits.h"
+#include "src/__support/FPUtil/multiply_add.h"
+#include "src/__support/FPUtil/nearest_integer.h"
+#include "src/__support/FPUtil/rounding_mode.h"
+#include "src/__support/common.h"
+#include "src/__support/macros/config.h"
+#include "src/__support/macros/optimization.h"
+#include "src/__support/math/exp2f_float_utils.h"
+
+namespace LIBC_NAMESPACE_DECL {
+namespace math {
+namespace float_eval {
+
+LIBC_INLINE float exp2f(float x) {
+ using FPBits = typename fputil::FPBits<float>;
+ FPBits xbits(x);
+
+ uint32_t x_u = xbits.uintval();
+ uint32_t x_abs = x_u & 0x7fff'ffffU;
+
+ // When |x| >= 128, or x is nan, or |x| <= 2^-25
+ if (LIBC_UNLIKELY(x_abs >= 0x4300'0000U || x_abs <= 0x3280'0000U)) {
+ // |x| <= 2^-25
+ if (x_abs <= 0x3280'0000U) {
+ return 1.0f + x;
+ }
+
+ // x >= 128
+ if (xbits.is_pos()) {
+ // x is finite
+ if (x_u < 0x7f80'0000U) {
+#ifndef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
+ int rounding = fputil::quick_get_round();
+ if (rounding == FE_DOWNWARD || rounding == FE_TOWARDZERO)
+ return FPBits::max_normal().get_val();
+#endif // LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
+
+ fputil::set_errno_if_required(ERANGE);
+ fputil::raise_except_if_required(FE_OVERFLOW);
+ }
+ // x is +inf or nan
+ return x + FPBits::inf().get_val();
+ }
+ // x <= -150
+ if (x_u >= 0xc316'0000U) {
+ // exp(-Inf) = 0
+ if (xbits.is_inf())
+ return 0.0f;
+ // exp(nan) = nan
+ if (xbits.is_nan())
+ return x;
+#ifndef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
+ if (fputil::fenv_is_round_up())
+ return FPBits::min_subnormal().get_val();
+#endif // LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
+ if (x != 0.0f) {
+ fputil::set_errno_if_required(ERANGE);
+ fputil::raise_except_if_required(FE_UNDERFLOW);
+ }
+ return 0.0f;
+ }
+ }
+
+ // Range reduction:
+ // k = round(x)
+ // x = k + u, with |u| <= 0.5
+ // 2^x = 2^k * 2^u
+ float kf = fputil::nearest_integer(x);
+ int k = static_cast<int>(kf);
+
+ float u = fputil::multiply_add(kf, -1.0f, x);
+ return exp2f_eval(u, k);
+}
+
+} // namespace float_eval
+} // namespace math
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC___SUPPORT_MATH_EXP2F_FLOAT_EVAL_H
diff --git a/libc/src/__support/math/exp2f_float_utils.h b/libc/src/__support/math/exp2f_float_utils.h
new file mode 100644
index 0000000000000..1c96050bea37f
--- /dev/null
+++ b/libc/src/__support/math/exp2f_float_utils.h
@@ -0,0 +1,107 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 single-precision exponential functions.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC___SUPPORT_MATH_EXP2F_FLOAT_UTILS_H
+#define LLVM_LIBC_SRC___SUPPORT_MATH_EXP2F_FLOAT_UTILS_H
+
+#include "src/__support/CPP/bit.h"
+#include "src/__support/FPUtil/multiply_add.h"
+#include "src/__support/macros/config.h"
+#include "src/__support/macros/optimization.h"
+
+namespace LIBC_NAMESPACE_DECL {
+namespace math {
+namespace float_eval {
+
+// Compute y * 2^m for y in [0.5, 2.0) and m in [-150, 128].
+//
+// We divide into three cases depending on m:
+// 1. -126 <= m <= 127:
+// 2^m is a normal float (exponent field m + 127 in [1, 254]).
+// We can directly compute y * 2^m with a single multiplication.
+// The condition is checked using unsigned comparison: (m + 126) <= 253.
+//
+// 2. m > 127 (i.e., m = 128):
+// 2^m cannot be represented as a finite normal float (exponent field
+// 128 + 127 = 255 is inf). Instead, we compute:
+// y * 2^m = (2 * y) * 2^(m - 1).
+// Since y < 2.0, 2 * y is exact (simply increments the exponent of y),
+// and 2^(m - 1) = 2^127 is a normal float.
+//
+// 3. m < -126 (i.e., m in [-150, -127]):
+// The result is in the subnormal range (or underflows to 0).
+// To avoid double-rounding errors, we scale in two steps:
+// y * 2^m = (y * 2^(m + 25)) * 2^-25.
+// Since m >= -150, m + 25 >= -125, so 2^(m + 25) is a normal float.
+// The intermediate product (y * 2^(m + 25)) is normal and computed with
+// full precision. Then the final multiplication by 2^-25 rounds directly
+// to the target subnormal precision in a single rounding step.
+LIBC_INLINE float scale_exp2f(float y, int m) {
+ // -126 <= m <= 127: 2^m is a normal float.
+ if (LIBC_LIKELY(static_cast<uint32_t>(m + 126) <= 253)) {
+ uint32_t scale_bits = static_cast<uint32_t>(m + 127) << 23;
+ float scale = cpp::bit_cast<float>(scale_bits);
+ return y * scale;
+ }
+
+ // m = 128: 2^m cannot be represented as a normal float.
+ // Compute (2 * y) * 2^(m - 1), where 2 * y is exact and 2^(m - 1) is normal.
+ if (m > 127) {
+ uint32_t scale_bits = static_cast<uint32_t>(m - 1 + 127) << 23;
+ float scale = cpp::bit_cast<float>(scale_bits);
+ return (y * 2.0f) * scale;
+ }
+
+ // Subnormal result: m in [-150, -127].
+ // Compute (y * 2^(m + 25)) * 2^-25 so that the first product is normal and
+ // the second multiplication rounds directly to the subnormal result.
+ uint32_t scale_bits = static_cast<uint32_t>(m + 25 + 127) << 23;
+ float scale = cpp::bit_cast<float>(scale_bits);
+ return (y * scale) * 0x1.0p-25f;
+}
+
+// Degree-6 polynomial approximating 2^u - 1 = u * P(u) on [-0.5, 0.5] using
+// Estrin's scheme and exponent scaling.
+// Evaluates 2^x = 2^m * 2^u.
+// Generated by Sollya with:
+// > display = hexadecimal;
+// > P = fpminimax((2^x - 1)/x, 5, [|SG...|], [-0.5, 0.5]);
+// > dirtyinfnorm(1 + x * P - 2^x, [-0.5, 0.5]);
+// 0x1.68cb...p-28
+// > dirtyinfnorm((1 + x * P - 2^x) / 2^x, [-0.5, 0.5]);
+// 0x1.5109...p-28
+LIBC_INLINE float exp2f_eval(float u, int m) {
+ constexpr float COEFFS[6] = {0x1.62e430p-1f, 0x1.ebfbdep-3f,
+ 0x1.c6af9ep-5f, 0x1.3b2c58p-7f,
+ 0x1.5ef4a2p-10f, 0x1.427918p-13f};
+
+ float u2 = u * u;
+ float u4 = u2 * u2;
+
+ float p0 = fputil::multiply_add(u, COEFFS[1], COEFFS[0]);
+ float p1 = fputil::multiply_add(u, COEFFS[3], COEFFS[2]);
+ float p2 = fputil::multiply_add(u, COEFFS[5], COEFFS[4]);
+
+ float q0 = fputil::multiply_add(u2, p1, p0);
+ float p = fputil::multiply_add(u4, p2, q0);
+
+ float y = fputil::multiply_add(u, p, 1.0f);
+
+ return scale_exp2f(y, m);
+}
+
+} // namespace float_eval
+} // namespace math
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC___SUPPORT_MATH_EXP2F_FLOAT_UTILS_H
diff --git a/libc/src/__support/math/expf.h b/libc/src/__support/math/expf.h
index f2c2c5319d221..a64b323e08b2d 100644
--- a/libc/src/__support/math/expf.h
+++ b/libc/src/__support/math/expf.h
@@ -1,120 +1,52 @@
-//===-- Implementation header for expf --------------------------*- C++ -*-===//
+//===----------------------------------------------------------------------===//
//
// 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
+/// Implementation header for expf.
+///
+//===----------------------------------------------------------------------===//
#ifndef LLVM_LIBC_SRC___SUPPORT_MATH_EXPF_H
#define LLVM_LIBC_SRC___SUPPORT_MATH_EXPF_H
-#include "exp_float_constants.h" // Lookup tables EXP_M1 and EXP_M2.
-#include "src/__support/FPUtil/FEnvImpl.h"
-#include "src/__support/FPUtil/FPBits.h"
-#include "src/__support/FPUtil/PolyEval.h"
-#include "src/__support/FPUtil/multiply_add.h"
-#include "src/__support/FPUtil/nearest_integer.h"
-#include "src/__support/FPUtil/rounding_mode.h"
#include "src/__support/common.h"
#include "src/__support/macros/config.h"
-#include "src/__support/macros/optimization.h" // LIBC_UNLIKELY
+#include "src/__support/macros/optimization.h"
+#include "src/__support/macros/properties/cpu_features.h"
-namespace LIBC_NAMESPACE_DECL {
-
-namespace math {
+#if defined(LIBC_MATH_HAS_SKIP_ACCURATE_PASS) && \
+ (defined(LIBC_MATH_HAS_SMALL_TABLES) || \
+ defined(LIBC_MATH_HAS_INTERMEDIATE_COMP_IN_FLOAT))
-LIBC_INLINE float expf(float x) {
- using FPBits = typename fputil::FPBits<float>;
- FPBits xbits(x);
+#include "src/__support/math/expf_float_eval.h"
+#define LIBC_MATH_EXPF_IMPL float_eval
- uint32_t x_u = xbits.uintval();
- uint32_t x_abs = x_u & 0x7fff'ffffU;
+#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)
-#ifndef LIBC_MATH_HAS_SKIP_ACCURATE_PASS
- // Exceptional values
- if (LIBC_UNLIKELY(x_u == 0xc236'bd8cU)) { // x = -0x1.6d7b18p+5f
- return 0x1.108a58p-66f - x * 0x1.0p-95f;
- }
-#endif // !LIBC_MATH_HAS_SKIP_ACCURATE_PASS
+#include "src/__support/math/expf_integer_eval.h"
+#define LIBC_MATH_EXPF_IMPL integer_eval
- // When |x| >= 89, |x| < 2^-25, or x is nan
- if (LIBC_UNLIKELY(x_abs >= 0x42b2'0000U || x_abs <= 0x3280'0000U)) {
- // |x| < 2^-25
- if (xbits.get_biased_exponent() <= 101) {
- return 1.0f + x;
- }
+#else
+#include "src/__support/math/expf_double_eval.h"
+#define LIBC_MATH_EXPF_IMPL double_eval
- // When x < log(2^-150) or nan
- if (xbits.uintval() >= 0xc2cf'f1b5U) {
- // exp(-Inf) = 0
- if (xbits.is_inf())
- return 0.0f;
- // exp(nan) = nan
- if (xbits.is_nan())
- return x;
-#ifndef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
- if (fputil::fenv_is_round_up())
- return FPBits::min_subnormal().get_val();
-#endif
- fputil::set_errno_if_required(ERANGE);
- fputil::raise_except_if_required(FE_UNDERFLOW);
- return 0.0f;
- }
- // x >= 89 or nan
- if (xbits.is_pos() && (xbits.uintval() >= 0x42b2'0000)) {
- // x is finite
- if (xbits.uintval() < 0x7f80'0000U) {
-#ifndef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
- int rounding = fputil::quick_get_round();
- if (rounding == FE_DOWNWARD || rounding == FE_TOWARDZERO)
- return FPBits::max_normal().get_val();
-#endif
+#endif // LIBC_MATH_HAS_SKIP_ACCURATE_PASS
- fputil::set_errno_if_required(ERANGE);
- fputil::raise_except_if_required(FE_OVERFLOW);
- }
- // x is +inf or nan
- return x + FPBits::inf().get_val();
- }
- }
- // For -104 < x < 89, to compute exp(x), we perform the following range
- // reduction: find hi, mid, lo such that:
- // x = hi + mid + lo, in which
- // hi is an integer,
- // mid * 2^7 is an integer
- // -2^(-8) <= lo < 2^-8.
- // In particular,
- // hi + mid = round(x * 2^7) * 2^(-7).
- // Then,
- // exp(x) = exp(hi + mid + lo) = exp(hi) * exp(mid) * exp(lo).
- // We store exp(hi) and exp(mid) in the lookup tables EXP_M1 and EXP_M2
- // respectively. exp(lo) is computed using a degree-4 minimax polynomial
- // generated by Sollya.
+namespace LIBC_NAMESPACE_DECL {
+namespace math {
- // x_hi = (hi + mid) * 2^7 = round(x * 2^7).
- float kf = fputil::nearest_integer(x * 0x1.0p7f);
- // Subtract (hi + mid) from x to get lo.
- double xd = static_cast<double>(fputil::multiply_add(kf, -0x1.0p-7f, x));
- int x_hi = static_cast<int>(kf);
- x_hi += 104 << 7;
- // hi = x_hi >> 7
- double exp_hi = EXP_M1[x_hi >> 7];
- // mid * 2^7 = x_hi & 0x0000'007fU;
- double exp_mid = EXP_M2[x_hi & 0x7f];
- // Degree-4 minimax polynomial generated by Sollya with the following
- // commands:
- // > display = hexadecimal;
- // > Q = fpminimax(expm1(x)/x, 3, [|D...|], [-2^-8, 2^-8]);
- // > Q;
- double exp_lo =
- fputil::polyeval(xd, 0x1p0, 0x1.ffffffffff777p-1, 0x1.000000000071cp-1,
- 0x1.555566668e5e7p-3, 0x1.55555555ef243p-5);
- return static_cast<float>(exp_hi * exp_mid * exp_lo);
-}
+using LIBC_MATH_EXPF_IMPL::expf;
} // namespace math
-
} // namespace LIBC_NAMESPACE_DECL
+#undef LIBC_MATH_EXPF_IMPL
+
#endif // LLVM_LIBC_SRC___SUPPORT_MATH_EXPF_H
diff --git a/libc/src/__support/math/expf_double_eval.h b/libc/src/__support/math/expf_double_eval.h
new file mode 100644
index 0000000000000..8a990bd67c265
--- /dev/null
+++ b/libc/src/__support/math/expf_double_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
+/// Double-precision implementation of expf.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC___SUPPORT_MATH_EXPF_DOUBLE_EVAL_H
+#define LLVM_LIBC_SRC___SUPPORT_MATH_EXPF_DOUBLE_EVAL_H
+
+#include "src/__support/FPUtil/FEnvImpl.h"
+#include "src/__support/FPUtil/FPBits.h"
+#include "src/__support/FPUtil/PolyEval.h"
+#include "src/__support/FPUtil/multiply_add.h"
+#include "src/__support/FPUtil/nearest_integer.h"
+#include "src/__support/FPUtil/rounding_mode.h"
+#include "src/__support/common.h"
+#include "src/__support/macros/config.h"
+#include "src/__support/macros/optimization.h"
+#include "src/__support/math/exp_float_constants.h" // Lookup tables EXP_M1 and EXP_M2.
+
+namespace LIBC_NAMESPACE_DECL {
+namespace math {
+namespace double_eval {
+
+LIBC_INLINE float expf(float x) {
+ using FPBits = typename fputil::FPBits<float>;
+ FPBits xbits(x);
+
+ uint32_t x_u = xbits.uintval();
+ uint32_t x_abs = x_u & 0x7fff'ffffU;
+
+#ifndef LIBC_MATH_HAS_SKIP_ACCURATE_PASS
+ // Exceptional values
+ if (LIBC_UNLIKELY(x_u == 0xc236'bd8cU)) { // x = -0x1.6d7b18p+5f
+ return 0x1.108a58p-66f - x * 0x1.0p-95f;
+ }
+#endif // !LIBC_MATH_HAS_SKIP_ACCURATE_PASS
+
+ // When |x| >= 89, |x| < 2^-25, or x is nan
+ if (LIBC_UNLIKELY(x_abs >= 0x42b2'0000U || x_abs <= 0x3280'0000U)) {
+ // |x| < 2^-25
+ if (xbits.get_biased_exponent() <= 101) {
+ return 1.0f + x;
+ }
+
+ // When x < log(2^-150) or nan
+ if (xbits.uintval() >= 0xc2cf'f1b5U) {
+ // exp(-Inf) = 0
+ if (xbits.is_inf())
+ return 0.0f;
+ // exp(nan) = nan
+ if (xbits.is_nan())
+ return x;
+#ifndef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
+ if (fputil::fenv_is_round_up())
+ return FPBits::min_subnormal().get_val();
+#endif // LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
+ fputil::set_errno_if_required(ERANGE);
+ fputil::raise_except_if_required(FE_UNDERFLOW);
+ return 0.0f;
+ }
+ // x >= 89 or nan
+ if (xbits.is_pos() && (xbits.uintval() >= 0x42b2'0000)) {
+ // x is finite
+ if (xbits.uintval() < 0x7f80'0000U) {
+#ifndef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
+ int rounding = fputil::quick_get_round();
+ if (rounding == FE_DOWNWARD || rounding == FE_TOWARDZERO)
+ return FPBits::max_normal().get_val();
+#endif // LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
+
+ fputil::set_errno_if_required(ERANGE);
+ fputil::raise_except_if_required(FE_OVERFLOW);
+ }
+ // x is +inf or nan
+ return x + FPBits::inf().get_val();
+ }
+ }
+ // For -104 < x < 89, to compute exp(x), we perform the following range
+ // reduction: find hi, mid, lo such that:
+ // x = hi + mid + lo, in which
+ // hi is an integer,
+ // mid * 2^7 is an integer
+ // -2^(-8) <= lo < 2^-8.
+ // In particular,
+ // hi + mid = round(x * 2^7) * 2^(-7).
+ // Then,
+ // exp(x) = exp(hi + mid + lo) = exp(hi) * exp(mid) * exp(lo).
+ // We store exp(hi) and exp(mid) in the lookup tables EXP_M1 and EXP_M2
+ // respectively. exp(lo) is computed using a degree-4 minimax polynomial
+ // generated by Sollya.
+
+ // x_hi = (hi + mid) * 2^7 = round(x * 2^7).
+ float kf = fputil::nearest_integer(x * 0x1.0p7f);
+ // Subtract (hi + mid) from x to get lo.
+ double xd = static_cast<double>(fputil::multiply_add(kf, -0x1.0p-7f, x));
+ int x_hi = static_cast<int>(kf);
+ x_hi += 104 << 7;
+ // hi = x_hi >> 7
+ double exp_hi = EXP_M1[x_hi >> 7];
+ // mid * 2^7 = x_hi & 0x0000'007fU;
+ double exp_mid = EXP_M2[x_hi & 0x7f];
+ // Degree-4 minimax polynomial generated by Sollya with the following
+ // commands:
+ // > display = hexadecimal;
+ // > Q = fpminimax(expm1(x)/x, 3, [|D...|], [-2^-8, 2^-8]);
+ // > Q;
+ double exp_lo =
+ fputil::polyeval(xd, 0x1p0, 0x1.ffffffffff777p-1, 0x1.000000000071cp-1,
+ 0x1.555566668e5e7p-3, 0x1.55555555ef243p-5);
+ return static_cast<float>(exp_hi * exp_mid * exp_lo);
+}
+
+} // namespace double_eval
+} // namespace math
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC___SUPPORT_MATH_EXPF_DOUBLE_EVAL_H
diff --git a/libc/src/__support/math/expf_float_eval.h b/libc/src/__support/math/expf_float_eval.h
new file mode 100644
index 0000000000000..5561e9d8e06f8
--- /dev/null
+++ b/libc/src/__support/math/expf_float_eval.h
@@ -0,0 +1,126 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+/// Float-only implementation of expf.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC___SUPPORT_MATH_EXPF_FLOAT_EVAL_H
+#define LLVM_LIBC_SRC___SUPPORT_MATH_EXPF_FLOAT_EVAL_H
+
+#include "src/__support/FPUtil/FEnvImpl.h"
+#include "src/__support/FPUtil/FPBits.h"
+#include "src/__support/FPUtil/double_double.h"
+#include "src/__support/FPUtil/multiply_add.h"
+#include "src/__support/FPUtil/nearest_integer.h"
+#include "src/__support/FPUtil/rounding_mode.h"
+#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"
+#include "src/__support/math/exp2f_float_utils.h"
+
+namespace LIBC_NAMESPACE_DECL {
+namespace math {
+namespace float_eval {
+
+LIBC_INLINE float expf(float x) {
+ using FPBits = typename fputil::FPBits<float>;
+ FPBits xbits(x);
+
+ uint32_t x_u = xbits.uintval();
+ uint32_t x_abs = x_u & 0x7fff'ffffU;
+
+ // When |x| >= 89, |x| < 2^-25, or x is nan
+ if (LIBC_UNLIKELY(x_abs >= 0x42b2'0000U || x_abs <= 0x3280'0000U)) {
+ // |x| < 2^-25
+ if (xbits.get_biased_exponent() <= 101) {
+ return 1.0f + x;
+ }
+
+ // When x < log(2^-150) or nan
+ if (xbits.uintval() >= 0xc2cf'f1b5U) {
+ // exp(-Inf) = 0
+ if (xbits.is_inf())
+ return 0.0f;
+ // exp(nan) = nan
+ if (xbits.is_nan())
+ return x;
+#ifndef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
+ if (fputil::fenv_is_round_up())
+ return FPBits::min_subnormal().get_val();
+#endif // LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
+ fputil::set_errno_if_required(ERANGE);
+ fputil::raise_except_if_required(FE_UNDERFLOW);
+ return 0.0f;
+ }
+ // x >= 89 or nan
+ if (xbits.is_pos() && (xbits.uintval() >= 0x42b2'0000)) {
+ // x is finite
+ if (xbits.uintval() < 0x7f80'0000U) {
+#ifndef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
+ int rounding = fputil::quick_get_round();
+ if (rounding == FE_DOWNWARD || rounding == FE_TOWARDZERO)
+ return FPBits::max_normal().get_val();
+#endif // LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
+
+ fputil::set_errno_if_required(ERANGE);
+ fputil::raise_except_if_required(FE_OVERFLOW);
+ }
+ // x is +inf or nan
+ return x + FPBits::inf().get_val();
+ }
+ }
+
+ // Range reduction:
+ // k = round(x * log2(e))
+ // x * log2(e) = k + u, with |u| <= 0.5
+ // e^x = 2^(k + u) = 2^k * 2^u
+#if defined(LIBC_TARGET_CPU_HAS_FMA_FLOAT)
+ // Constants generated by Sollya with:
+ // > display = hexadecimal;
+ // > hi = round(log2(exp(1)), SG, RN);
+ // > lo = round(log2(exp(1)) - hi, SG, RN);
+ constexpr fputil::FloatFloat LOG2_E = {/*lo=*/0x1.4ae0cp-26f,
+ /*hi=*/0x1.715476p+0f};
+
+ float kf = fputil::nearest_integer(x * LOG2_E.hi);
+ int k = static_cast<int>(kf);
+
+ float u_hi = fputil::multiply_add(x, LOG2_E.hi, -kf);
+ float u = fputil::multiply_add(x, LOG2_E.lo, u_hi);
+#else // !LIBC_TARGET_CPU_HAS_FMA_FLOAT
+ // Cody-Waite reduction for non-FMA targets:
+ // Constants generated by Sollya with:
+ // > display = hexadecimal;
+ // > LOG2_E = round(log2(exp(1)), SG, RN);
+ // > LOG_2_HI = round(log(2), 12, RN);
+ // > LOG_2_LO = round(log(2) - LOG_2_HI, SG, RN);
+ constexpr float LOG2_E = 0x1.715476p+0f;
+ constexpr float LOG_2_HI = 0x1.62ep-1f;
+ constexpr float LOG_2_LO = 0x1.0bfbe8p-15f;
+
+ float kf = fputil::nearest_integer(x * LOG2_E);
+ int k = static_cast<int>(kf);
+
+ float v_hi = fputil::multiply_add(-kf, LOG_2_HI, x);
+ float v = fputil::multiply_add(-kf, LOG_2_LO, v_hi);
+
+ // Convert reduced argument to base-2: u = v * log2(e)
+ float u = v * LOG2_E;
+#endif // LIBC_TARGET_CPU_HAS_FMA_FLOAT
+
+ return exp2f_eval(u, k);
+}
+
+} // namespace float_eval
+} // namespace math
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC___SUPPORT_MATH_EXPF_FLOAT_EVAL_H
diff --git a/libc/src/__support/math/expf_integer_eval.h b/libc/src/__support/math/expf_integer_eval.h
index e48b459f880b1..c072abaf96955 100644
--- a/libc/src/__support/math/expf_integer_eval.h
+++ b/libc/src/__support/math/expf_integer_eval.h
@@ -15,6 +15,7 @@
#ifndef LLVM_LIBC_SRC___SUPPORT_MATH_EXPF_INTEGER_EVAL_H
#define LLVM_LIBC_SRC___SUPPORT_MATH_EXPF_INTEGER_EVAL_H
+#include "hdr/fenv_macros.h"
#include "src/__support/CPP/bit.h"
#include "src/__support/FPUtil/FPBits.h"
#include "src/__support/FPUtil/PolyEval.h"
@@ -74,7 +75,7 @@ LIBC_INLINE float expf(float x, [[maybe_unused]] int rounding) {
if (x_val_abs <= 0x3300'0000U) {
#ifdef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
return 1.0f;
-#else
+#else // !LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
if (x_val_abs == 0)
return 1.0f;
@@ -113,7 +114,7 @@ LIBC_INLINE float expf(float x, [[maybe_unused]] int rounding) {
return FPBits::max_normal().get_val();
return FPBits::inf().get_val();
-#else
+#else // LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
return FPBits::inf().get_val();
#endif // !LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
}
@@ -248,7 +249,7 @@ LIBC_INLINE float expf(float x, [[maybe_unused]] int rounding) {
result += e_y_unbiased;
return cpp::bit_cast<float>(result);
-#else
+#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));
@@ -270,7 +271,7 @@ LIBC_INLINE float expf(float x, [[maybe_unused]] int rounding) {
result += e_y_unbiased;
return cpp::bit_cast<float>(result);
-#endif // !LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
+#endif // LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
}
} // namespace static_rounding
@@ -279,6 +280,16 @@ LIBC_INLINE float expf(float x, [[maybe_unused]] int rounding) {
} // namespace shared
+namespace math {
+namespace integer_eval {
+
+LIBC_INLINE float expf(float x) {
+ return shared::math::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/src/math/generic/expf.cpp b/libc/src/math/generic/expf.cpp
index 1ec621b22677f..83324cb51e7ca 100644
--- a/libc/src/math/generic/expf.cpp
+++ b/libc/src/math/generic/expf.cpp
@@ -7,19 +7,11 @@
//===----------------------------------------------------------------------===//
#include "src/math/expf.h"
-#include "shared/math/static_rounding/expf.h"
+#include "src/__support/common.h"
#include "src/__support/math/expf.h"
namespace LIBC_NAMESPACE_DECL {
-LLVM_LIBC_FUNCTION(float, expf, (float x)) {
-#if !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)
- return shared::math::static_rounding::expf(x, FE_TONEAREST);
-#else
- return math::expf(x);
-#endif
-}
+LLVM_LIBC_FUNCTION(float, expf, (float x)) { return math::expf(x); }
} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/test/src/math/CMakeLists.txt b/libc/test/src/math/CMakeLists.txt
index 8f1a543de0a5e..1fd0772aa5031 100644
--- a/libc/test/src/math/CMakeLists.txt
+++ b/libc/test/src/math/CMakeLists.txt
@@ -1357,22 +1357,16 @@ add_fp_unittest(
SRCS
expf_test.cpp
DEPENDS
+ libc.hdr.errno_macros
+ libc.hdr.fenv_macros
libc.src.errno.errno
libc.src.math.expf
+ libc.src.__support.math.expf_double_eval
+ libc.src.__support.math.expf_float_eval
+ libc.src.__support.math.expf_integer_eval
libc.src.__support.FPUtil.fp_bits
)
-add_fp_unittest(
- expf_static_rounding_test
- NEED_MPFR
- SUITE
- libc-math-unittests
- SRCS
- expf_static_rounding_test.cpp
- DEPENDS
- libc.src.errno.errno
- libc.src.__support.FPUtil.fp_bits
-)
add_fp_unittest(
expf16_test
@@ -1417,8 +1411,12 @@ add_fp_unittest(
SRCS
exp2f_test.cpp
DEPENDS
+ libc.hdr.errno_macros
+ libc.hdr.fenv_macros
libc.src.errno.errno
libc.src.math.exp2f
+ libc.src.__support.math.exp2f_double_eval
+ libc.src.__support.math.exp2f_float_eval
libc.src.__support.FPUtil.fp_bits
)
@@ -1478,8 +1476,12 @@ add_fp_unittest(
SRCS
exp10f_test.cpp
DEPENDS
+ libc.hdr.errno_macros
+ libc.hdr.fenv_macros
libc.src.errno.errno
libc.src.math.exp10f
+ libc.src.__support.math.exp10f_double_eval
+ libc.src.__support.math.exp10f_float_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 3d30a96922f69..2d42bee4c6540 100644
--- a/libc/test/src/math/exhaustive/CMakeLists.txt
+++ b/libc/test/src/math/exhaustive/CMakeLists.txt
@@ -263,22 +263,10 @@ add_fp_unittest(
expf_test.cpp
DEPENDS
.exhaustive_test
- libc.src.math.expf
- libc.src.__support.FPUtil.fp_bits
- LINK_LIBRARIES
- -lpthread
-)
-
-add_fp_unittest(
- expf_static_rounding_test
- NO_RUN_POSTBUILD
- SUITE
- libc_math_exhaustive_tests
- SRCS
- expf_static_rounding_test.cpp
- DEPENDS
.exhaustive_test_static_rounding
- libc.src.__support.math.expf
+ libc.src.math.expf
+ libc.src.__support.math.expf_double_eval
+ libc.src.__support.math.expf_float_eval
libc.src.__support.math.expf_integer_eval
libc.src.__support.FPUtil.fp_bits
LINK_LIBRARIES
@@ -296,6 +284,7 @@ add_fp_unittest(
DEPENDS
.exhaustive_test
libc.src.math.exp2f
+ libc.src.__support.math.exp2f_float_eval
libc.src.__support.FPUtil.fp_bits
LINK_LIBRARIES
-lpthread
@@ -327,6 +316,7 @@ add_fp_unittest(
DEPENDS
.exhaustive_test
libc.src.math.exp10f
+ libc.src.__support.math.exp10f_float_eval
libc.src.__support.FPUtil.fp_bits
LINK_LIBRARIES
-lpthread
@@ -720,6 +710,7 @@ add_fp_unittest(
libc.src.__support.math.atanf
)
+
add_fp_unittest(
asinf_test
NO_RUN_POSTBUILD
diff --git a/libc/test/src/math/exhaustive/exhaustive_test.h b/libc/test/src/math/exhaustive/exhaustive_test.h
index 9bf0c407219f1..85c0e07e3417f 100644
--- a/libc/test/src/math/exhaustive/exhaustive_test.h
+++ b/libc/test/src/math/exhaustive/exhaustive_test.h
@@ -11,6 +11,9 @@
///
//===----------------------------------------------------------------------===//
+#ifndef LLVM_LIBC_TEST_SRC_MATH_EXHAUSTIVE_EXHAUSTIVE_TEST_H
+#define LLVM_LIBC_TEST_SRC_MATH_EXHAUSTIVE_EXHAUSTIVE_TEST_H
+
#include "src/__support/CPP/type_traits.h"
#include "src/__support/FPUtil/FPBits.h"
#include "src/__support/macros/properties/types.h"
@@ -74,6 +77,62 @@ struct UnaryOpChecker : public virtual LIBC_NAMESPACE::testing::Test {
}
};
+template <typename OutType, typename InType,
+ UnaryOp<OutType, InType> BaselineFunc, UnaryOp<OutType, InType> Func,
+ unsigned Tolerance = 0>
+struct UnaryOpAgainstBaselineChecker
+ : public virtual LIBC_NAMESPACE::testing::Test {
+ using FloatType = InType;
+ using FPBits = LIBC_NAMESPACE::fputil::FPBits<FloatType>;
+ using StorageType = typename FPBits::StorageType;
+
+ // Check in a range, return the number of failures.
+ uint64_t check(StorageType start, StorageType stop,
+ mpfr::RoundingMode rounding) {
+ mpfr::ForceRoundingMode r(rounding);
+ if (!r.success)
+ return (stop > start);
+ StorageType bits = start;
+ uint64_t failed = 0;
+ do {
+ FPBits xbits(bits);
+ FloatType x = xbits.get_val();
+ OutType result = Func(x);
+ OutType expected = BaselineFunc(x);
+ using OutFPBits = LIBC_NAMESPACE::fputil::FPBits<OutType>;
+ using OutStorageType = typename OutFPBits::StorageType;
+ OutFPBits result_bits(result);
+ OutFPBits expected_bits(expected);
+
+ bool correct = false;
+ if (expected_bits.is_nan()) {
+ correct = result_bits.is_nan();
+ } else if (result_bits.is_nan()) {
+ correct = false;
+ } else if (Tolerance == 0) {
+ correct = (result_bits.uintval() == expected_bits.uintval());
+ } else {
+ OutStorageType diff = 0;
+ if (expected_bits.sign() == result_bits.sign()) {
+ OutStorageType u1 = expected_bits.uintval();
+ OutStorageType u2 = result_bits.uintval();
+ diff = (u1 > u2) ? (u1 - u2) : (u2 - u1);
+ } else {
+ diff = (expected_bits.uintval() & OutFPBits::EXP_SIG_MASK) +
+ (result_bits.uintval() & OutFPBits::EXP_SIG_MASK);
+ }
+ correct = (diff <= Tolerance);
+ }
+ failed += (!correct);
+ // Uncomment to print out failed values.
+ if (!correct) {
+ EXPECT_FP_EQ(expected, result);
+ }
+ } while (bits++ < stop);
+ return failed;
+ }
+};
+
template <typename OutType, typename InType = OutType>
using BinaryOp = OutType(InType, InType);
@@ -272,6 +331,12 @@ template <typename FloatType, mpfr::Operation Op, UnaryOp<FloatType> Func,
using LlvmLibcUnaryOpExhaustiveMathTest = LlvmLibcExhaustiveMathTest<
UnaryOpChecker<FloatType, FloatType, Op, Func, Tolerance>>;
+template <typename FloatType, UnaryOp<FloatType> BaselineFunc,
+ UnaryOp<FloatType> Func, unsigned Tolerance = 0>
+using LlvmLibcUnaryOpAgainstBaselineExhaustiveMathTest =
+ LlvmLibcExhaustiveMathTest<UnaryOpAgainstBaselineChecker<
+ FloatType, FloatType, BaselineFunc, Func, Tolerance>>;
+
template <typename OutType, typename InType, mpfr::Operation Op,
UnaryOp<OutType, InType> Func>
using LlvmLibcUnaryNarrowingOpExhaustiveMathTest =
@@ -281,3 +346,5 @@ template <typename FloatType, mpfr::Operation Op, BinaryOp<FloatType> Func>
using LlvmLibcBinaryOpExhaustiveMathTest =
LlvmLibcExhaustiveMathTest<BinaryOpChecker<FloatType, FloatType, Op, Func>,
1 << 2>;
+
+#endif // LLVM_LIBC_TEST_SRC_MATH_EXHAUSTIVE_EXHAUSTIVE_TEST_H
diff --git a/libc/test/src/math/exhaustive/exhaustive_test_static_rounding.h b/libc/test/src/math/exhaustive/exhaustive_test_static_rounding.h
index d268a6fdfd806..5b135c822e667 100644
--- a/libc/test/src/math/exhaustive/exhaustive_test_static_rounding.h
+++ b/libc/test/src/math/exhaustive/exhaustive_test_static_rounding.h
@@ -12,10 +12,12 @@
///
//===----------------------------------------------------------------------===//
+#ifndef LLVM_LIBC_TEST_SRC_MATH_EXHAUSTIVE_EXHAUSTIVE_TEST_STATIC_ROUNDING_H
+#define LLVM_LIBC_TEST_SRC_MATH_EXHAUSTIVE_EXHAUSTIVE_TEST_STATIC_ROUNDING_H
+
// This file is modeled after exhaustive_test.h, modified for testing statically
// rounded math functions.
-#include "exhaustive_test.h"
#include "src/__support/CPP/type_traits.h"
#include "src/__support/FPUtil/FPBits.h"
#include "src/__support/macros/properties/types.h"
@@ -23,6 +25,7 @@
#include "test/UnitTest/RoundingModeUtils.h"
#include "test/UnitTest/Test.h"
#include "test/UnitTest/TestLogger.h"
+#include "test/src/math/exhaustive/exhaustive_test.h"
#include <atomic>
#include <iostream>
@@ -234,3 +237,5 @@ template <typename FloatType, UnaryOp<FloatType> BaselineFunc,
using LlvmLibcStaticallyRoundedUnaryOpExhaustiveMathTest =
LlvmLibcExhaustiveStaticallyRoundedMathTest<StaticallyRoundedUnaryOpChecker<
FloatType, FloatType, BaselineFunc, Func>>;
+
+#endif // LLVM_LIBC_TEST_SRC_MATH_EXHAUSTIVE_EXHAUSTIVE_TEST_STATIC_ROUNDING_H
diff --git a/libc/test/src/math/exhaustive/exp10f_test.cpp b/libc/test/src/math/exhaustive/exp10f_test.cpp
index eb1e9164a6cd3..150e3d0812b65 100644
--- a/libc/test/src/math/exhaustive/exp10f_test.cpp
+++ b/libc/test/src/math/exhaustive/exp10f_test.cpp
@@ -6,28 +6,47 @@
//
//===----------------------------------------------------------------------===//
-#include "exhaustive_test.h"
+#include "src/__support/math/exp10f_float_eval.h"
#include "src/math/exp10f.h"
+#include "test/src/math/exhaustive/exhaustive_test.h"
#include "utils/MPFRWrapper/MPFRUtils.h"
namespace mpfr = LIBC_NAMESPACE::testing::mpfr;
-using LlvmLibcExp10fExhaustiveTest =
- LlvmLibcUnaryOpExhaustiveMathTest<float, mpfr::Operation::Exp10,
- LIBC_NAMESPACE::exp10f>;
-
// Range: [0, Inf];
static constexpr uint32_t POS_START = 0x0000'0000U;
static constexpr uint32_t POS_STOP = 0x7f80'0000U;
+// Range: [-Inf, 0];
+static constexpr uint32_t NEG_START = 0x8000'0000U;
+static constexpr uint32_t NEG_STOP = 0xff80'0000U;
+
+using LlvmLibcExp10fExhaustiveTest =
+ LlvmLibcUnaryOpExhaustiveMathTest<float, mpfr::Operation::Exp10,
+ LIBC_NAMESPACE::exp10f>;
+
TEST_F(LlvmLibcExp10fExhaustiveTest, PositiveRange) {
test_full_range_all_roundings(POS_START, POS_STOP);
}
-// Range: [-Inf, 0];
-static constexpr uint32_t NEG_START = 0xb000'0000U;
-static constexpr uint32_t NEG_STOP = 0xff80'0000U;
-
TEST_F(LlvmLibcExp10fExhaustiveTest, NegativeRange) {
test_full_range_all_roundings(NEG_START, NEG_STOP);
}
+
+// Float-eval implementation: tested against the correctly rounded double
+// precision version for round-to-nearest with 1 ULP bound.
+static float exp10f_float_eval(float x) {
+ return LIBC_NAMESPACE::math::float_eval::exp10f(x);
+}
+
+using LlvmLibcExp10fFloatExhaustiveTest =
+ LlvmLibcUnaryOpAgainstBaselineExhaustiveMathTest<
+ float, LIBC_NAMESPACE::exp10f, exp10f_float_eval, 1>;
+
+TEST_F(LlvmLibcExp10fFloatExhaustiveTest, PositiveRange) {
+ test_full_range(mpfr::RoundingMode::Nearest, POS_START, POS_STOP);
+}
+
+TEST_F(LlvmLibcExp10fFloatExhaustiveTest, NegativeRange) {
+ test_full_range(mpfr::RoundingMode::Nearest, 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 fcf2a4c0c0e26..33f94adb48455 100644
--- a/libc/test/src/math/exhaustive/exp2f_test.cpp
+++ b/libc/test/src/math/exhaustive/exp2f_test.cpp
@@ -6,28 +6,47 @@
//
//===----------------------------------------------------------------------===//
-#include "exhaustive_test.h"
+#include "src/__support/math/exp2f_float_eval.h"
#include "src/math/exp2f.h"
+#include "test/src/math/exhaustive/exhaustive_test.h"
#include "utils/MPFRWrapper/MPFRUtils.h"
namespace mpfr = LIBC_NAMESPACE::testing::mpfr;
-using LlvmLibcExp2fExhaustiveTest =
- LlvmLibcUnaryOpExhaustiveMathTest<float, mpfr::Operation::Exp2,
- LIBC_NAMESPACE::exp2f>;
-
// Range: [0, Inf];
static constexpr uint32_t POS_START = 0x0000'0000U;
static constexpr uint32_t POS_STOP = 0x7f80'0000U;
+// Range: [-Inf, 0];
+static constexpr uint32_t NEG_START = 0x8000'0000U;
+static constexpr uint32_t NEG_STOP = 0xff80'0000U;
+
+using LlvmLibcExp2fExhaustiveTest =
+ LlvmLibcUnaryOpExhaustiveMathTest<float, mpfr::Operation::Exp2,
+ LIBC_NAMESPACE::exp2f>;
+
TEST_F(LlvmLibcExp2fExhaustiveTest, PositiveRange) {
test_full_range_all_roundings(POS_START, POS_STOP);
}
-// Range: [-Inf, 0];
-static constexpr uint32_t NEG_START = 0xb000'0000U;
-static constexpr uint32_t NEG_STOP = 0xff80'0000U;
-
TEST_F(LlvmLibcExp2fExhaustiveTest, NegativeRange) {
test_full_range_all_roundings(NEG_START, NEG_STOP);
}
+
+// Float-eval implementation: tested against the correctly rounded double
+// precision version for round-to-nearest with 1 ULP bound.
+static float exp2f_float_eval(float x) {
+ return LIBC_NAMESPACE::math::float_eval::exp2f(x);
+}
+
+using LlvmLibcExp2fFloatExhaustiveTest =
+ LlvmLibcUnaryOpAgainstBaselineExhaustiveMathTest<
+ float, LIBC_NAMESPACE::exp2f, exp2f_float_eval, 1>;
+
+TEST_F(LlvmLibcExp2fFloatExhaustiveTest, PositiveRange) {
+ test_full_range(mpfr::RoundingMode::Nearest, POS_START, POS_STOP);
+}
+
+TEST_F(LlvmLibcExp2fFloatExhaustiveTest, NegativeRange) {
+ test_full_range(mpfr::RoundingMode::Nearest, NEG_START, NEG_STOP);
+}
diff --git a/libc/test/src/math/exhaustive/expf_static_rounding_test.cpp b/libc/test/src/math/exhaustive/expf_static_rounding_test.cpp
deleted file mode 100644
index 1ab2884a6b29e..0000000000000
--- a/libc/test/src/math/exhaustive/expf_static_rounding_test.cpp
+++ /dev/null
@@ -1,39 +0,0 @@
-
-//===----------------------------------------------------------------------===//
-//
-// 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 exhaustive tests for statically-rounded
-/// implementation of static_rounding::expf(x)
-///
-//===----------------------------------------------------------------------===//
-
-#include "exhaustive_test_static_rounding.h"
-#include "src/__support/math/expf.h"
-#include "src/__support/math/expf_integer_eval.h"
-
-using LlvmLibcStaticallyRoundedExpfExhaustiveTest =
- LlvmLibcStaticallyRoundedUnaryOpExhaustiveMathTest<
- float, LIBC_NAMESPACE::math::expf,
- LIBC_NAMESPACE::shared::math::static_rounding::expf>;
-
-// Range: [0, Inf];
-static constexpr uint32_t POS_START = 0x0000'0000U;
-static constexpr uint32_t POS_STOP = 0x7f80'0000U;
-
-TEST_F(LlvmLibcStaticallyRoundedExpfExhaustiveTest, PositiveRange) {
- test_full_range_all_roundings(POS_START, POS_STOP);
-}
-
-// Range: [-Inf, 0];
-static constexpr uint32_t NEG_START = 0xb000'0000U;
-static constexpr uint32_t NEG_STOP = 0xff80'0000U;
-
-TEST_F(LlvmLibcStaticallyRoundedExpfExhaustiveTest, 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 90285a1036d8e..27aafd7d9ce7a 100644
--- a/libc/test/src/math/exhaustive/expf_test.cpp
+++ b/libc/test/src/math/exhaustive/expf_test.cpp
@@ -6,28 +6,65 @@
//
//===----------------------------------------------------------------------===//
-#include "exhaustive_test.h"
+#include "src/__support/math/expf_double_eval.h"
+#include "src/__support/math/expf_float_eval.h"
+#include "src/__support/math/expf_integer_eval.h"
#include "src/math/expf.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;
-using LlvmLibcExpfExhaustiveTest =
- LlvmLibcUnaryOpExhaustiveMathTest<float, mpfr::Operation::Exp,
- LIBC_NAMESPACE::expf>;
-
// Range: [0, Inf];
static constexpr uint32_t POS_START = 0x0000'0000U;
static constexpr uint32_t POS_STOP = 0x7f80'0000U;
+// Range: [-Inf, 0];
+static constexpr uint32_t NEG_START = 0x8000'0000U;
+static constexpr uint32_t NEG_STOP = 0xff80'0000U;
+
+using LlvmLibcExpfExhaustiveTest =
+ LlvmLibcUnaryOpExhaustiveMathTest<float, mpfr::Operation::Exp,
+ LIBC_NAMESPACE::expf>;
+
TEST_F(LlvmLibcExpfExhaustiveTest, PositiveRange) {
test_full_range_all_roundings(POS_START, POS_STOP);
}
-// Range: [-Inf, 0];
-static constexpr uint32_t NEG_START = 0xb000'0000U;
-static constexpr uint32_t NEG_STOP = 0xff80'0000U;
-
TEST_F(LlvmLibcExpfExhaustiveTest, NegativeRange) {
test_full_range_all_roundings(NEG_START, NEG_STOP);
}
+
+// Float-eval implementation: tested against the correctly rounded double
+// precision version for round-to-nearest with 1 ULP bound.
+static float expf_float_eval(float x) {
+ return LIBC_NAMESPACE::math::float_eval::expf(x);
+}
+
+using LlvmLibcExpfFloatExhaustiveTest =
+ LlvmLibcUnaryOpAgainstBaselineExhaustiveMathTest<
+ float, LIBC_NAMESPACE::expf, expf_float_eval, 1>;
+
+TEST_F(LlvmLibcExpfFloatExhaustiveTest, PositiveRange) {
+ test_full_range(mpfr::RoundingMode::Nearest, POS_START, POS_STOP);
+}
+
+TEST_F(LlvmLibcExpfFloatExhaustiveTest, NegativeRange) {
+ test_full_range(mpfr::RoundingMode::Nearest, NEG_START, NEG_STOP);
+}
+
+// Statically rounded implementation: tested against double_eval across all
+// roundings.
+using LlvmLibcExpfStaticRoundingExhaustiveTest =
+ LlvmLibcStaticallyRoundedUnaryOpExhaustiveMathTest<
+ float, LIBC_NAMESPACE::math::double_eval::expf,
+ LIBC_NAMESPACE::shared::math::static_rounding::expf>;
+
+TEST_F(LlvmLibcExpfStaticRoundingExhaustiveTest, PositiveRange) {
+ test_full_range_all_roundings(POS_START, POS_STOP);
+}
+
+TEST_F(LlvmLibcExpfStaticRoundingExhaustiveTest, NegativeRange) {
+ test_full_range_all_roundings(NEG_START, NEG_STOP);
+}
diff --git a/libc/test/src/math/exp10f_test.cpp b/libc/test/src/math/exp10f_test.cpp
index 4dbe04845257e..763c61a1c66c9 100644
--- a/libc/test/src/math/exp10f_test.cpp
+++ b/libc/test/src/math/exp10f_test.cpp
@@ -1,123 +1,181 @@
-//===-- Unittests for exp10f ----------------------------------------------===//
+//===----------------------------------------------------------------------===//
//
// 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 unittests for exp10f.
+///
+//===----------------------------------------------------------------------===//
+#include "hdr/errno_macros.h"
+#include "hdr/fenv_macros.h"
#include "hdr/math_macros.h"
+#include "hdr/stdint_proxy.h"
#include "src/__support/FPUtil/FPBits.h"
#include "src/__support/libc_errno.h"
+#include "src/__support/macros/optimization.h"
+#include "src/__support/math/exp10f_double_eval.h"
+#include "src/__support/math/exp10f_float_eval.h"
#include "src/math/exp10f.h"
#include "test/UnitTest/FPMatcher.h"
#include "test/UnitTest/Test.h"
#include "utils/MPFRWrapper/MPFRUtils.h"
-#include "hdr/stdint_proxy.h"
+namespace mpfr = LIBC_NAMESPACE::testing::mpfr;
-using LlvmLibcExp10fTest = LIBC_NAMESPACE::testing::FPTest<float>;
+class Exp10fTest : public LIBC_NAMESPACE::testing::FPTest<float> {
+public:
+ void test_special_numbers(float (*func)(float)) {
+ EXPECT_FP_EQ(aNaN, func(aNaN));
+ EXPECT_MATH_ERRNO(0);
-namespace mpfr = LIBC_NAMESPACE::testing::mpfr;
+ EXPECT_FP_EQ(inf, func(inf));
+ EXPECT_MATH_ERRNO(0);
+
+ EXPECT_FP_EQ(0.0f, func(neg_inf));
+ EXPECT_MATH_ERRNO(0);
-TEST_F(LlvmLibcExp10fTest, SpecialNumbers) {
- EXPECT_FP_EQ(aNaN, LIBC_NAMESPACE::exp10f(aNaN));
- EXPECT_MATH_ERRNO(0);
-
- EXPECT_FP_EQ(inf, LIBC_NAMESPACE::exp10f(inf));
- EXPECT_MATH_ERRNO(0);
-
- EXPECT_FP_EQ(0.0f, LIBC_NAMESPACE::exp10f(neg_inf));
- EXPECT_MATH_ERRNO(0);
-
- EXPECT_FP_EQ(1.0f, LIBC_NAMESPACE::exp10f(0.0f));
- EXPECT_MATH_ERRNO(0);
-
- EXPECT_FP_EQ(1.0f, LIBC_NAMESPACE::exp10f(-0.0f));
- EXPECT_MATH_ERRNO(0);
-}
-
-TEST_F(LlvmLibcExp10fTest, Overflow) {
- EXPECT_FP_EQ_WITH_EXCEPTION(
- inf, LIBC_NAMESPACE::exp10f(FPBits(0x7f7fffffU).get_val()), FE_OVERFLOW);
- EXPECT_MATH_ERRNO(ERANGE);
-
- EXPECT_FP_EQ_WITH_EXCEPTION(
- inf, LIBC_NAMESPACE::exp10f(FPBits(0x43000000U).get_val()), FE_OVERFLOW);
- EXPECT_MATH_ERRNO(ERANGE);
-
- EXPECT_FP_EQ_WITH_EXCEPTION(
- inf, LIBC_NAMESPACE::exp10f(FPBits(0x43000001U).get_val()), FE_OVERFLOW);
- EXPECT_MATH_ERRNO(ERANGE);
-}
-
-TEST_F(LlvmLibcExp10fTest, Underflow) {
- EXPECT_FP_EQ_WITH_EXCEPTION(
- 0.0f, LIBC_NAMESPACE::exp10f(FPBits(0xff7fffffU).get_val()),
- FE_UNDERFLOW);
- EXPECT_MATH_ERRNO(ERANGE);
-
- float x = FPBits(0xc2cffff8U).get_val();
- EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Exp10, x,
- LIBC_NAMESPACE::exp10f(x), 0.5);
- EXPECT_MATH_ERRNO(ERANGE);
-
- x = FPBits(0xc2d00008U).get_val();
- EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Exp10, x,
- LIBC_NAMESPACE::exp10f(x), 0.5);
- EXPECT_MATH_ERRNO(ERANGE);
-}
-
-TEST_F(LlvmLibcExp10fTest, TrickyInputs) {
- constexpr int N = 20;
- constexpr uint32_t INPUTS[N] = {
- 0x325e5bd8, // x = 0x1.bcb7bp-27f
- 0x325e5bd9, // x = 0x1.bcb7b2p-27f
- 0x325e5bda, // x = 0x1.bcb7b4p-27f
- 0x3d14d956, // x = 0x1.29b2acp-5f
- 0x4116498a, // x = 0x1.2c9314p3f
- 0x4126f431, // x = 0x1.4de862p3f
- 0x4187d13c, // x = 0x1.0fa278p4f
- 0x4203e9da, // x = 0x1.07d3b4p5f
- 0x420b5f5d, // x = 0x1.16bebap5f
- 0x42349e35, // x = 0x1.693c6ap5f
- 0x3f800000, // x = 1.0f
- 0x40000000, // x = 2.0f
- 0x40400000, // x = 3.0f
- 0x40800000, // x = 4.0f
- 0x40a00000, // x = 5.0f
- 0x40c00000, // x = 6.0f
- 0x40e00000, // x = 7.0f
- 0x41000000, // x = 8.0f
- 0x41100000, // x = 9.0f
- 0x41200000, // x = 10.0f
- };
- for (int i = 0; i < N; ++i) {
- float x = FPBits(INPUTS[i]).get_val();
- EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Exp10, x,
- LIBC_NAMESPACE::exp10f(x), 0.5);
- EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Exp10, -x,
- LIBC_NAMESPACE::exp10f(-x), 0.5);
+ EXPECT_FP_EQ(1.0f, func(0.0f));
+ EXPECT_MATH_ERRNO(0);
+
+ EXPECT_FP_EQ(1.0f, func(-0.0f));
+ EXPECT_MATH_ERRNO(0);
}
-}
-
-TEST_F(LlvmLibcExp10fTest, InFloatRange) {
- constexpr uint32_t COUNT = 1'231;
- constexpr uint32_t STEP = UINT32_MAX / COUNT;
- for (uint32_t i = 0, v = 0; i <= COUNT; ++i, v += STEP) {
- float x = FPBits(v).get_val();
- if (FPBits(v).is_nan() || FPBits(v).is_inf())
- continue;
- libc_errno = 0;
- float result = LIBC_NAMESPACE::exp10f(x);
-
- // If the computation resulted in an error or did not produce valid result
- // in the single-precision floating point range, then ignore comparing with
- // MPFR result as MPFR can still produce valid results because of its
- // wider precision.
- if (FPBits(result).is_nan() || FPBits(result).is_inf() || libc_errno != 0)
- continue;
- ASSERT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Exp10, x,
- LIBC_NAMESPACE::exp10f(x), 0.5);
+
+ void test_overflow(float (*func)(float),
+ bool check_exception_and_errno = true) {
+ constexpr float VALUES[] = {
+ FPBits(0x7f7fffffU).get_val(),
+ FPBits(0x43000000U).get_val(),
+ FPBits(0x43000001U).get_val(),
+ };
+ for (float x : VALUES) {
+ if (check_exception_and_errno) {
+ EXPECT_FP_EQ_WITH_EXCEPTION(inf, func(x), FE_OVERFLOW);
+ EXPECT_MATH_ERRNO(ERANGE);
+ } else {
+ EXPECT_FP_EQ(inf, func(x));
+ }
+ }
}
-}
+
+ void test_underflow(float (*func)(float), double ulp_tolerance = 0.5,
+ bool all_rounding = true,
+ bool check_exception_and_errno = true) {
+ if (check_exception_and_errno) {
+ EXPECT_FP_EQ_WITH_EXCEPTION(0.0f, func(FPBits(0xff7fffffU).get_val()),
+ FE_UNDERFLOW);
+ EXPECT_MATH_ERRNO(ERANGE);
+ } else {
+ EXPECT_FP_EQ(0.0f, func(FPBits(0xff7fffffU).get_val()));
+ }
+
+ constexpr float VALUES[] = {
+ FPBits(0xc2cffff8U).get_val(),
+ FPBits(0xc2d00008U).get_val(),
+ };
+
+ for (float x : VALUES) {
+ if (all_rounding) {
+ EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Exp10, x, func(x),
+ ulp_tolerance);
+ } else {
+ EXPECT_MPFR_MATCH(mpfr::Operation::Exp10, x, func(x), ulp_tolerance);
+ }
+ if (check_exception_and_errno) {
+ EXPECT_MATH_ERRNO(ERANGE);
+ }
+ }
+ }
+
+ void test_tricky_inputs(float (*func)(float), double ulp_tolerance = 0.5,
+ bool all_rounding = true) {
+ constexpr int N = 20;
+ constexpr uint32_t INPUTS[N] = {
+ 0x325e5bd8, // x = 0x1.bcb7bp-27f
+ 0x325e5bd9, // x = 0x1.bcb7b2p-27f
+ 0x325e5bda, // x = 0x1.bcb7b4p-27f
+ 0x3d14d956, // x = 0x1.29b2acp-5f
+ 0x4116498a, // x = 0x1.2c9314p3f
+ 0x4126f431, // x = 0x1.4de862p3f
+ 0x4187d13c, // x = 0x1.0fa278p4f
+ 0x4203e9da, // x = 0x1.07d3b4p5f
+ 0x420b5f5d, // x = 0x1.16bebap5f
+ 0x42349e35, // x = 0x1.693c6ap5f
+ 0x3f800000, // x = 1.0f
+ 0x40000000, // x = 2.0f
+ 0x40400000, // x = 3.0f
+ 0x40800000, // x = 4.0f
+ 0x40a00000, // x = 5.0f
+ 0x40c00000, // x = 6.0f
+ 0x40e00000, // x = 7.0f
+ 0x41000000, // x = 8.0f
+ 0x41100000, // x = 9.0f
+ 0x41200000, // x = 10.0f
+ };
+ for (int i = 0; i < N; ++i) {
+ float x = FPBits(INPUTS[i]).get_val();
+ if (all_rounding) {
+ EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Exp10, x, func(x),
+ ulp_tolerance);
+ EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Exp10, -x, func(-x),
+ ulp_tolerance);
+ } else {
+ EXPECT_MPFR_MATCH(mpfr::Operation::Exp10, x, func(x), ulp_tolerance);
+ EXPECT_MPFR_MATCH(mpfr::Operation::Exp10, -x, func(-x), ulp_tolerance);
+ }
+ }
+ }
+
+ void test_in_range(float (*func)(float), double ulp_tolerance = 0.5,
+ bool all_rounding = true, bool check_errno = true) {
+ constexpr uint32_t COUNT = 1'231;
+ constexpr uint32_t STEP = UINT32_MAX / COUNT;
+ for (uint32_t i = 0, v = 0; i <= COUNT; ++i, v += STEP) {
+ float x = FPBits(v).get_val();
+ if (FPBits(v).is_nan() || FPBits(v).is_inf())
+ continue;
+
+ libc_errno = 0;
+ float result = func(x);
+ if (FPBits(result).is_nan() || FPBits(result).is_inf())
+ continue;
+ if (check_errno && libc_errno != 0)
+ continue;
+
+ if (all_rounding) {
+ EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Exp10, x, func(x),
+ ulp_tolerance);
+ } else {
+ EXPECT_MPFR_MATCH(mpfr::Operation::Exp10, x, result, ulp_tolerance);
+ }
+ }
+ }
+};
+
+#define LIST_EXP10F_TESTS(suffix, func, ulp_tolerance, all_rounding) \
+ using LlvmLibcExp10fTest##suffix = Exp10fTest; \
+ TEST_F(LlvmLibcExp10fTest##suffix, SpecialNumbers) { \
+ test_special_numbers(&func); \
+ } \
+ TEST_F(LlvmLibcExp10fTest##suffix, Overflow) { test_overflow(&func); } \
+ TEST_F(LlvmLibcExp10fTest##suffix, Underflow) { \
+ test_underflow(&func, ulp_tolerance, all_rounding); \
+ } \
+ 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); \
+ }
+
+LIST_EXP10F_TESTS(Default, LIBC_NAMESPACE::exp10f, /*ulp_tolerance=*/0.5,
+ /*all_rounding=*/true)
+LIST_EXP10F_TESTS(DoubleEval, LIBC_NAMESPACE::math::double_eval::exp10f,
+ /*ulp_tolerance=*/0.5, /*all_rounding=*/true)
+LIST_EXP10F_TESTS(FloatEval, LIBC_NAMESPACE::math::float_eval::exp10f,
+ /*ulp_tolerance=*/1.5, /*all_rounding=*/false)
diff --git a/libc/test/src/math/exp2f_test.cpp b/libc/test/src/math/exp2f_test.cpp
index 7f54fbce31505..d5ed25b8aa092 100644
--- a/libc/test/src/math/exp2f_test.cpp
+++ b/libc/test/src/math/exp2f_test.cpp
@@ -1,16 +1,25 @@
-//===-- Unittests for exp2f -----------------------------------------------===//
+//===----------------------------------------------------------------------===//
//
// 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 unittests for exp2f.
+///
+//===----------------------------------------------------------------------===//
+#include "hdr/errno_macros.h"
+#include "hdr/fenv_macros.h"
#include "hdr/math_macros.h"
#include "hdr/stdint_proxy.h"
#include "src/__support/FPUtil/FPBits.h"
#include "src/__support/libc_errno.h"
#include "src/__support/macros/optimization.h"
+#include "src/__support/math/exp2f_double_eval.h"
+#include "src/__support/math/exp2f_float_eval.h"
#include "src/math/exp2f.h"
#include "test/UnitTest/FPMatcher.h"
#include "test/UnitTest/Test.h"
@@ -18,108 +27,157 @@
#ifdef LIBC_MATH_HAS_SKIP_ACCURATE_PASS
#define TOLERANCE 1
-#else
+#else // !LIBC_MATH_HAS_SKIP_ACCURATE_PASS
#define TOLERANCE 0
#endif // LIBC_MATH_HAS_SKIP_ACCURATE_PASS
-using LlvmLibcExp2fTest = LIBC_NAMESPACE::testing::FPTest<float>;
-
namespace mpfr = LIBC_NAMESPACE::testing::mpfr;
-TEST_F(LlvmLibcExp2fTest, SpecialNumbers) {
- EXPECT_FP_EQ(aNaN, LIBC_NAMESPACE::exp2f(aNaN));
- EXPECT_MATH_ERRNO(0);
-
- EXPECT_FP_EQ(inf, LIBC_NAMESPACE::exp2f(inf));
- EXPECT_MATH_ERRNO(0);
-
- EXPECT_FP_EQ(0.0f, LIBC_NAMESPACE::exp2f(neg_inf));
- EXPECT_MATH_ERRNO(0);
-
- EXPECT_FP_EQ(1.0f, LIBC_NAMESPACE::exp2f(0.0f));
- EXPECT_MATH_ERRNO(0);
-
- EXPECT_FP_EQ(1.0f, LIBC_NAMESPACE::exp2f(-0.0f));
- EXPECT_MATH_ERRNO(0);
-}
-
-TEST_F(LlvmLibcExp2fTest, Overflow) {
- EXPECT_FP_EQ_WITH_EXCEPTION(
- inf, LIBC_NAMESPACE::exp2f(FPBits(0x7f7fffffU).get_val()), FE_OVERFLOW);
- EXPECT_MATH_ERRNO(ERANGE);
-
- EXPECT_FP_EQ_WITH_EXCEPTION(
- inf, LIBC_NAMESPACE::exp2f(FPBits(0x43000000U).get_val()), FE_OVERFLOW);
- EXPECT_MATH_ERRNO(ERANGE);
-
- EXPECT_FP_EQ_WITH_EXCEPTION(
- inf, LIBC_NAMESPACE::exp2f(FPBits(0x43000001U).get_val()), FE_OVERFLOW);
- EXPECT_MATH_ERRNO(ERANGE);
-}
-
-TEST_F(LlvmLibcExp2fTest, TrickyInputs) {
- constexpr int N = 12;
- constexpr uint32_t INPUTS[N] = {
- 0x3b429d37U, /*0x1.853a6ep-9f*/
- 0x3c02a9adU, /*0x1.05535ap-7f*/
- 0x3ca66e26U, /*0x1.4cdc4cp-6f*/
- 0x3d92a282U, /*0x1.254504p-4f*/
- 0x42fa0001U, /*0x1.f40002p+6f*/
- 0x42ffffffU, /*0x1.fffffep+6f*/
- 0xb8d3d026U, /*-0x1.a7a04cp-14f*/
- 0xbcf3a937U, /*-0x1.e7526ep-6f*/
- 0xc2fa0001U, /*-0x1.f40002p+6f*/
- 0xc2fc0000U, /*-0x1.f8p+6f*/
- 0xc2fc0001U, /*-0x1.f80002p+6f*/
- 0xc3150000U, /*-0x1.2ap+7f*/
- };
- for (int i = 0; i < N; ++i) {
- libc_errno = 0;
- float x = FPBits(INPUTS[i]).get_val();
- EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Exp2, x,
- LIBC_NAMESPACE::exp2f(x), TOLERANCE + 0.5);
+class Exp2fTest : public LIBC_NAMESPACE::testing::FPTest<float> {
+public:
+ void test_special_numbers(float (*func)(float)) {
+ EXPECT_FP_EQ(aNaN, func(aNaN));
+ EXPECT_MATH_ERRNO(0);
+
+ EXPECT_FP_EQ(inf, func(inf));
+ EXPECT_MATH_ERRNO(0);
+
+ EXPECT_FP_EQ(0.0f, func(neg_inf));
+ EXPECT_MATH_ERRNO(0);
+
+ EXPECT_FP_EQ(1.0f, func(0.0f));
+ EXPECT_MATH_ERRNO(0);
+
+ EXPECT_FP_EQ(1.0f, func(-0.0f));
EXPECT_MATH_ERRNO(0);
}
-}
-
-TEST_F(LlvmLibcExp2fTest, Underflow) {
- EXPECT_FP_EQ_WITH_EXCEPTION(
- 0.0f, LIBC_NAMESPACE::exp2f(FPBits(0xff7fffffU).get_val()), FE_UNDERFLOW);
- EXPECT_MATH_ERRNO(ERANGE);
-
- float x = FPBits(0xc3158000U).get_val();
- EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Exp2, x,
- LIBC_NAMESPACE::exp2f(x), 0.5);
- EXPECT_MATH_ERRNO(0);
-
- x = FPBits(0xc3160000U).get_val();
- EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Exp2, x,
- LIBC_NAMESPACE::exp2f(x), 0.5);
- EXPECT_MATH_ERRNO(ERANGE);
-
- x = FPBits(0xc3165432U).get_val();
- EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Exp2, x,
- LIBC_NAMESPACE::exp2f(x), 0.5);
- EXPECT_MATH_ERRNO(ERANGE);
-}
-
-TEST_F(LlvmLibcExp2fTest, InFloatRange) {
- constexpr uint32_t COUNT = 1'231;
- constexpr uint32_t STEP = UINT32_MAX / COUNT;
- for (uint32_t i = 0, v = 0; i <= COUNT; ++i, v += STEP) {
- float x = FPBits(v).get_val();
- if (FPBits(v).is_nan() || FPBits(v).is_inf())
- continue;
- libc_errno = 0;
- float result = LIBC_NAMESPACE::exp2f(x);
-
- // If the computation resulted in an error or did not produce valid result
- // in the single-precision floating point range, then ignore comparing with
- // MPFR result as MPFR can still produce valid results because of its
- // wider precision.
- if (FPBits(result).is_nan() || FPBits(result).is_inf() || libc_errno != 0)
- continue;
- ASSERT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Exp2, x,
- LIBC_NAMESPACE::exp2f(x), 0.5);
+
+ void test_overflow(float (*func)(float),
+ bool check_exception_and_errno = true) {
+ constexpr float VALUES[] = {
+ FPBits(0x7f7fffffU).get_val(),
+ FPBits(0x43000000U).get_val(),
+ FPBits(0x43000001U).get_val(),
+ };
+ for (float x : VALUES) {
+ if (check_exception_and_errno) {
+ EXPECT_FP_EQ_WITH_EXCEPTION(inf, func(x), FE_OVERFLOW);
+ EXPECT_MATH_ERRNO(ERANGE);
+ } else {
+ EXPECT_FP_EQ(inf, func(x));
+ }
+ }
+ }
+
+ void test_tricky_inputs(float (*func)(float), double ulp_tolerance = 0.5,
+ bool all_rounding = true) {
+ constexpr int N = 12;
+ constexpr uint32_t INPUTS[N] = {
+ 0x3b429d37U, /*0x1.853a6ep-9f*/
+ 0x3c02a9adU, /*0x1.05535ap-7f*/
+ 0x3ca66e26U, /*0x1.4cdc4cp-6f*/
+ 0x3d92a282U, /*0x1.254504p-4f*/
+ 0x42fa0001U, /*0x1.f40002p+6f*/
+ 0x42ffffffU, /*0x1.fffffep+6f*/
+ 0xb8d3d026U, /*-0x1.a7a04cp-14f*/
+ 0xbcf3a937U, /*-0x1.e7526ep-6f*/
+ 0xc2fa0001U, /*-0x1.f40002p+6f*/
+ 0xc2fc0000U, /*-0x1.f8p+6f*/
+ 0xc2fc0001U, /*-0x1.f80002p+6f*/
+ 0xc3150000U, /*-0x1.2ap+7f*/
+ };
+ for (int i = 0; i < N; ++i) {
+ float x = FPBits(INPUTS[i]).get_val();
+ libc_errno = 0;
+ if (all_rounding) {
+ EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Exp2, x, func(x),
+ ulp_tolerance);
+ } else {
+ EXPECT_MPFR_MATCH(mpfr::Operation::Exp2, x, func(x), ulp_tolerance);
+ }
+ EXPECT_MATH_ERRNO(0);
+ }
+ }
+
+ void test_underflow(float (*func)(float), double ulp_tolerance = 0.5,
+ bool all_rounding = true,
+ bool check_exception_and_errno = true) {
+ if (check_exception_and_errno) {
+ EXPECT_FP_EQ_WITH_EXCEPTION(0.0f, func(FPBits(0xff7fffffU).get_val()),
+ FE_UNDERFLOW);
+ EXPECT_MATH_ERRNO(ERANGE);
+ } else {
+ EXPECT_FP_EQ(0.0f, func(FPBits(0xff7fffffU).get_val()));
+ }
+
+ struct TestCase {
+ float x;
+ int expected_errno;
+ } cases[] = {
+ {FPBits(0xc3158000U).get_val(), 0},
+ {FPBits(0xc3160000U).get_val(), ERANGE},
+ {FPBits(0xc3165432U).get_val(), ERANGE},
+ };
+
+ for (const auto &c : cases) {
+ libc_errno = 0;
+ if (all_rounding) {
+ EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Exp2, c.x, func(c.x),
+ ulp_tolerance);
+ } else {
+ EXPECT_MPFR_MATCH(mpfr::Operation::Exp2, c.x, func(c.x), ulp_tolerance);
+ }
+ if (check_exception_and_errno) {
+ EXPECT_MATH_ERRNO(c.expected_errno);
+ }
+ }
+ }
+
+ void test_in_range(float (*func)(float), double ulp_tolerance = 0.5,
+ bool all_rounding = true, bool check_errno = true) {
+ constexpr uint32_t COUNT = 1'231;
+ constexpr uint32_t STEP = UINT32_MAX / COUNT;
+ for (uint32_t i = 0, v = 0; i <= COUNT; ++i, v += STEP) {
+ float x = FPBits(v).get_val();
+ if (FPBits(v).is_nan() || FPBits(v).is_inf())
+ continue;
+
+ libc_errno = 0;
+ float result = func(x);
+ if (FPBits(result).is_nan() || FPBits(result).is_inf())
+ continue;
+ if (check_errno && libc_errno != 0)
+ continue;
+
+ if (all_rounding) {
+ EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Exp2, x, func(x),
+ ulp_tolerance);
+ } else {
+ EXPECT_MPFR_MATCH(mpfr::Operation::Exp2, x, result, ulp_tolerance);
+ }
+ }
}
-}
+};
+
+#define LIST_EXP2F_TESTS(suffix, func, ulp_tolerance, all_rounding) \
+ using LlvmLibcExp2fTest##suffix = Exp2fTest; \
+ TEST_F(LlvmLibcExp2fTest##suffix, SpecialNumbers) { \
+ test_special_numbers(&func); \
+ } \
+ TEST_F(LlvmLibcExp2fTest##suffix, Overflow) { test_overflow(&func); } \
+ TEST_F(LlvmLibcExp2fTest##suffix, TrickyInputs) { \
+ test_tricky_inputs(&func, ulp_tolerance, all_rounding); \
+ } \
+ TEST_F(LlvmLibcExp2fTest##suffix, Underflow) { \
+ test_underflow(&func, ulp_tolerance, all_rounding); \
+ } \
+ TEST_F(LlvmLibcExp2fTest##suffix, InFloatRange) { \
+ test_in_range(&func, ulp_tolerance, all_rounding); \
+ }
+
+LIST_EXP2F_TESTS(Default, LIBC_NAMESPACE::exp2f,
+ /*ulp_tolerance=*/TOLERANCE + 0.5, /*all_rounding=*/true)
+LIST_EXP2F_TESTS(DoubleEval, LIBC_NAMESPACE::math::double_eval::exp2f,
+ /*ulp_tolerance=*/TOLERANCE + 0.5, /*all_rounding=*/true)
+LIST_EXP2F_TESTS(FloatEval, LIBC_NAMESPACE::math::float_eval::exp2f,
+ /*ulp_tolerance=*/1.5, /*all_rounding=*/false)
diff --git a/libc/test/src/math/expf_static_rounding_test.cpp b/libc/test/src/math/expf_static_rounding_test.cpp
deleted file mode 100644
index 9befb1de10972..0000000000000
--- a/libc/test/src/math/expf_static_rounding_test.cpp
+++ /dev/null
@@ -1,133 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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 unit tests for statically-rounded implementation of
-/// static_rounding::expf(x)
-///
-//===----------------------------------------------------------------------===//
-
-#include "hdr/math_macros.h"
-#include "hdr/stdint_proxy.h"
-#include "shared/static_rounding_math.h"
-#include "src/__support/FPUtil/FPBits.h"
-#include "src/__support/libc_errno.h"
-#include "src/__support/macros/optimization.h"
-#include "src/__support/math/expf.h"
-#include "test/UnitTest/FPMatcher.h"
-#include "test/UnitTest/RoundingModeUtils.h"
-#include "test/UnitTest/Test.h"
-
-using LlvmLibcExpfStaticRoundingTest = LIBC_NAMESPACE::testing::FPTest<float>;
-using RoundingMode = LIBC_NAMESPACE::fputil::testing::RoundingMode;
-
-namespace static_rounding = LIBC_NAMESPACE::shared::math::static_rounding;
-namespace math = LIBC_NAMESPACE::math;
-
-TEST_F(LlvmLibcExpfStaticRoundingTest, SpecialNumbers) {
- using LIBC_NAMESPACE::fputil::testing::get_fe_rounding;
-
- for (auto rounding : ROUNDING_MODES) {
- const int fenv_rounding = get_fe_rounding(rounding);
- EXPECT_FP_EQ_ROUNDING_MODE(
- math::expf(aNaN), static_rounding::expf(aNaN, fenv_rounding), rounding);
- EXPECT_MATH_ERRNO(0);
-
- EXPECT_FP_EQ_ROUNDING_MODE(
- math::expf(inf), static_rounding::expf(inf, fenv_rounding), rounding);
- EXPECT_MATH_ERRNO(0);
-
- EXPECT_FP_EQ_ROUNDING_MODE(math::expf(neg_inf),
- static_rounding::expf(neg_inf, fenv_rounding),
- rounding);
- EXPECT_MATH_ERRNO(0);
-
- EXPECT_FP_EQ_ROUNDING_MODE(
- math::expf(0.0f), static_rounding::expf(0.0f, fenv_rounding), rounding);
- EXPECT_MATH_ERRNO(0);
-
- EXPECT_FP_EQ_ROUNDING_MODE(math::expf(-0.0f),
- static_rounding::expf(-0.0f, fenv_rounding),
- rounding);
- EXPECT_MATH_ERRNO(0);
- }
-}
-
-TEST_F(LlvmLibcExpfStaticRoundingTest, Overflow) {
- constexpr float VALUES[] = {FPBits(0x7f7fffffU).get_val(),
- FPBits(0x42cffff8U).get_val(),
- FPBits(0x42d00008U).get_val()};
-
- for (auto rounding : ROUNDING_MODES) {
- const int fenv_rounding = get_fe_rounding(rounding);
-
- // Statically rounded expf doesn't raise exceptions
-
- for (auto x : VALUES) {
- EXPECT_FP_EQ_ROUNDING_MODE(
- math::expf(x), static_rounding::expf(x, fenv_rounding), rounding);
- }
- }
-}
-
-TEST_F(LlvmLibcExpfStaticRoundingTest, Underflow) {
- using LIBC_NAMESPACE::fputil::testing::get_fe_rounding;
-
- constexpr float VALUES[] = {FPBits(0xff7fffffU).get_val(),
- FPBits(0xc2cffff8U).get_val(),
- FPBits(0xc2d00008U).get_val()};
-
- for (auto rounding : ROUNDING_MODES) {
- const int fenv_rounding = get_fe_rounding(rounding);
-
- // Statically rounded expf doesn't raise exceptions
- for (auto x : VALUES) {
- EXPECT_FP_EQ_ROUNDING_MODE(
- math::expf(x), static_rounding::expf(x, fenv_rounding), rounding);
- }
- }
-}
-
-// Test with inputs which are the borders of underflow/overflow but still
-// produce valid results without setting errno.
-TEST_F(LlvmLibcExpfStaticRoundingTest, Borderline) {
- using LIBC_NAMESPACE::fputil::testing::get_fe_rounding;
-
- constexpr float VALUES[] = {
- FPBits(0x42affff8U).get_val(), FPBits(0x42b00008U).get_val(),
- FPBits(0xc2affff8U).get_val(), FPBits(0xc2b00008U).get_val(),
- FPBits(0xc236bd8cU).get_val()};
-
- for (auto rounding : ROUNDING_MODES) {
- const int fenv_rounding = get_fe_rounding(rounding);
-
- for (auto x : VALUES) {
- EXPECT_FP_EQ_ROUNDING_MODE(
- math::expf(x), static_rounding::expf(x, fenv_rounding), rounding);
- }
- }
-}
-
-TEST_F(LlvmLibcExpfStaticRoundingTest, InFloatRange) {
- using LIBC_NAMESPACE::fputil::testing::get_fe_rounding;
-
- constexpr uint32_t COUNT = 1'231;
- constexpr uint32_t STEP = UINT32_MAX / COUNT;
- for (auto rounding : ROUNDING_MODES) {
- const int fenv_rounding = get_fe_rounding(rounding);
-
- for (uint32_t i = 0, v = 0; i <= COUNT; ++i, v += STEP) {
- float x = FPBits(v).get_val();
- if (FPBits(v).is_nan() || FPBits(v).is_inf())
- continue;
- libc_errno = 0;
- EXPECT_FP_EQ_ROUNDING_MODE(
- math::expf(x), static_rounding::expf(x, fenv_rounding), rounding);
- }
- }
-}
diff --git a/libc/test/src/math/expf_test.cpp b/libc/test/src/math/expf_test.cpp
index b04473d2bfe7a..2cf30559adabb 100644
--- a/libc/test/src/math/expf_test.cpp
+++ b/libc/test/src/math/expf_test.cpp
@@ -1,16 +1,26 @@
-//===-- Unittests for expf ------------------------------------------------===//
+//===----------------------------------------------------------------------===//
//
// 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 unittests for expf.
+///
+//===----------------------------------------------------------------------===//
+#include "hdr/errno_macros.h"
+#include "hdr/fenv_macros.h"
#include "hdr/math_macros.h"
#include "hdr/stdint_proxy.h"
#include "src/__support/FPUtil/FPBits.h"
#include "src/__support/libc_errno.h"
#include "src/__support/macros/optimization.h"
+#include "src/__support/math/expf_double_eval.h"
+#include "src/__support/math/expf_float_eval.h"
+#include "src/__support/math/expf_integer_eval.h"
#include "src/math/expf.h"
#include "test/UnitTest/FPMatcher.h"
#include "test/UnitTest/Test.h"
@@ -18,109 +28,167 @@
#ifdef LIBC_MATH_HAS_SKIP_ACCURATE_PASS
#define TOLERANCE 1
-#else
+#else // !LIBC_MATH_HAS_SKIP_ACCURATE_PASS
#define TOLERANCE 0
#endif // LIBC_MATH_HAS_SKIP_ACCURATE_PASS
-using LlvmLibcExpfTest = LIBC_NAMESPACE::testing::FPTest<float>;
-
namespace mpfr = LIBC_NAMESPACE::testing::mpfr;
-TEST_F(LlvmLibcExpfTest, SpecialNumbers) {
- EXPECT_FP_EQ(aNaN, LIBC_NAMESPACE::expf(aNaN));
- EXPECT_MATH_ERRNO(0);
-
- EXPECT_FP_EQ(inf, LIBC_NAMESPACE::expf(inf));
- EXPECT_MATH_ERRNO(0);
+class ExpfTest : public LIBC_NAMESPACE::testing::FPTest<float> {
+public:
+ void test_special_numbers(float (*func)(float), bool check_errno = true) {
+ EXPECT_FP_EQ(aNaN, func(aNaN));
+ if (check_errno)
+ EXPECT_MATH_ERRNO(0);
- EXPECT_FP_EQ(0.0f, LIBC_NAMESPACE::expf(neg_inf));
- EXPECT_MATH_ERRNO(0);
+ EXPECT_FP_EQ(inf, func(inf));
+ if (check_errno)
+ EXPECT_MATH_ERRNO(0);
- EXPECT_FP_EQ(1.0f, LIBC_NAMESPACE::expf(0.0f));
- EXPECT_MATH_ERRNO(0);
+ EXPECT_FP_EQ(0.0f, func(neg_inf));
+ if (check_errno)
+ EXPECT_MATH_ERRNO(0);
- EXPECT_FP_EQ(1.0f, LIBC_NAMESPACE::expf(-0.0f));
- EXPECT_MATH_ERRNO(0);
-}
-
-TEST_F(LlvmLibcExpfTest, Overflow) {
- EXPECT_FP_EQ_WITH_EXCEPTION(
- inf, LIBC_NAMESPACE::expf(FPBits(0x7f7fffffU).get_val()), FE_OVERFLOW);
- EXPECT_MATH_ERRNO(ERANGE);
+ EXPECT_FP_EQ(1.0f, func(0.0f));
+ if (check_errno)
+ EXPECT_MATH_ERRNO(0);
- EXPECT_FP_EQ_WITH_EXCEPTION(
- inf, LIBC_NAMESPACE::expf(FPBits(0x42cffff8U).get_val()), FE_OVERFLOW);
- EXPECT_MATH_ERRNO(ERANGE);
+ EXPECT_FP_EQ(1.0f, func(-0.0f));
+ if (check_errno)
+ EXPECT_MATH_ERRNO(0);
+ }
- EXPECT_FP_EQ_WITH_EXCEPTION(
- inf, LIBC_NAMESPACE::expf(FPBits(0x42d00008U).get_val()), FE_OVERFLOW);
- EXPECT_MATH_ERRNO(ERANGE);
-}
+ void test_overflow(float (*func)(float),
+ bool check_exception_and_errno = true) {
+ constexpr float VALUES[] = {
+ FPBits(0x7f7fffffU).get_val(),
+ FPBits(0x42cffff8U).get_val(),
+ FPBits(0x42d00008U).get_val(),
+ };
+ for (float x : VALUES) {
+ if (check_exception_and_errno) {
+ EXPECT_FP_EQ_WITH_EXCEPTION(inf, func(x), FE_OVERFLOW);
+ EXPECT_MATH_ERRNO(ERANGE);
+ } else {
+ EXPECT_FP_EQ(inf, func(x));
+ }
+ }
+ }
-TEST_F(LlvmLibcExpfTest, Underflow) {
- EXPECT_FP_EQ_WITH_EXCEPTION(
- 0.0f, LIBC_NAMESPACE::expf(FPBits(0xff7fffffU).get_val()), FE_UNDERFLOW);
- EXPECT_MATH_ERRNO(ERANGE);
+ void test_underflow(float (*func)(float), double ulp_tolerance = 0.5,
+ bool all_rounding = true,
+ bool check_exception_and_errno = true) {
+ if (check_exception_and_errno) {
+ EXPECT_FP_EQ_WITH_EXCEPTION(0.0f, func(FPBits(0xff7fffffU).get_val()),
+ FE_UNDERFLOW);
+ EXPECT_MATH_ERRNO(ERANGE);
+ } else {
+ EXPECT_FP_EQ(0.0f, func(FPBits(0xff7fffffU).get_val()));
+ }
+
+ constexpr float VALUES[] = {
+ FPBits(0xc2cffff8U).get_val(),
+ FPBits(0xc2d00008U).get_val(),
+ };
+
+ for (float x : VALUES) {
+ if (all_rounding) {
+ EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Exp, x, func(x),
+ ulp_tolerance);
+ } else {
+ EXPECT_MPFR_MATCH(mpfr::Operation::Exp, x, func(x), ulp_tolerance);
+ }
+ if (check_exception_and_errno) {
+ EXPECT_MATH_ERRNO(ERANGE);
+ }
+ }
+ }
- float x = FPBits(0xc2cffff8U).get_val();
- EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Exp, x,
- LIBC_NAMESPACE::expf(x), 0.5);
- EXPECT_MATH_ERRNO(ERANGE);
+ void test_borderline(float (*func)(float), double ulp_tolerance = 0.5,
+ bool all_rounding = true, bool check_errno = true) {
+ constexpr float INPUTS[] = {
+ FPBits(0x42affff8U).get_val(), FPBits(0x42b00008U).get_val(),
+ FPBits(0xc2affff8U).get_val(), FPBits(0xc2b00008U).get_val(),
+ FPBits(0xc236bd8cU).get_val()};
+
+ for (float x : INPUTS) {
+ if (all_rounding) {
+ ASSERT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Exp, x, func(x),
+ ulp_tolerance);
+ } else {
+ ASSERT_MPFR_MATCH(mpfr::Operation::Exp, x, func(x), ulp_tolerance);
+ }
+ if (check_errno) {
+ EXPECT_MATH_ERRNO(0);
+ }
+ }
+ }
- x = FPBits(0xc2d00008U).get_val();
- EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Exp, x,
- LIBC_NAMESPACE::expf(x), 0.5);
- EXPECT_MATH_ERRNO(ERANGE);
-}
+ void test_in_range(float (*func)(float), double ulp_tolerance = 0.5,
+ bool all_rounding = true, bool check_errno = true) {
+ constexpr uint32_t COUNT = 1'231;
+ constexpr uint32_t STEP = UINT32_MAX / COUNT;
+ for (uint32_t i = 0, v = 0; i <= COUNT; ++i, v += STEP) {
+ float x = FPBits(v).get_val();
+ if (FPBits(v).is_nan() || FPBits(v).is_inf())
+ continue;
+
+ libc_errno = 0;
+ float result = func(x);
+ if (FPBits(result).is_nan() || FPBits(result).is_inf())
+ continue;
+ if (check_errno && libc_errno != 0)
+ continue;
+
+ if (all_rounding) {
+ EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Exp, x, func(x),
+ ulp_tolerance);
+ } else {
+ EXPECT_MPFR_MATCH(mpfr::Operation::Exp, x, result, ulp_tolerance);
+ }
+ }
+ }
+};
+
+#define LIST_EXPF_TESTS(suffix, func, ulp_tolerance, all_rounding, \
+ check_exception_and_errno, check_errno) \
+ using LlvmLibcExpfTest##suffix = ExpfTest; \
+ TEST_F(LlvmLibcExpfTest##suffix, SpecialNumbers) { \
+ test_special_numbers(&func, check_errno); \
+ } \
+ TEST_F(LlvmLibcExpfTest##suffix, Overflow) { \
+ test_overflow(&func, check_exception_and_errno); \
+ } \
+ TEST_F(LlvmLibcExpfTest##suffix, Underflow) { \
+ test_underflow(&func, ulp_tolerance, all_rounding, \
+ check_exception_and_errno); \
+ } \
+ TEST_F(LlvmLibcExpfTest##suffix, Borderline) { \
+ test_borderline(&func, ulp_tolerance, all_rounding, check_errno); \
+ } \
+ TEST_F(LlvmLibcExpfTest##suffix, InFloatRange) { \
+ test_in_range(&func, ulp_tolerance, all_rounding, check_errno); \
+ }
-// Test with inputs which are the borders of underflow/overflow but still
-// produce valid results without setting errno.
-TEST_F(LlvmLibcExpfTest, Borderline) {
- float x;
-
- x = FPBits(0x42affff8U).get_val();
- ASSERT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Exp, x,
- LIBC_NAMESPACE::expf(x), 0.5);
- EXPECT_MATH_ERRNO(0);
-
- x = FPBits(0x42b00008U).get_val();
- ASSERT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Exp, x,
- LIBC_NAMESPACE::expf(x), 0.5);
- EXPECT_MATH_ERRNO(0);
-
- x = FPBits(0xc2affff8U).get_val();
- ASSERT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Exp, x,
- LIBC_NAMESPACE::expf(x), 0.5);
- EXPECT_MATH_ERRNO(0);
-
- x = FPBits(0xc2b00008U).get_val();
- ASSERT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Exp, x,
- LIBC_NAMESPACE::expf(x), 0.5);
- EXPECT_MATH_ERRNO(0);
-
- x = FPBits(0xc236bd8cU).get_val();
- EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Exp, x,
- LIBC_NAMESPACE::expf(x), TOLERANCE + 0.5);
- EXPECT_MATH_ERRNO(0);
+LIST_EXPF_TESTS(Default, LIBC_NAMESPACE::expf,
+ /*ulp_tolerance=*/TOLERANCE + 0.5,
+ /*all_rounding=*/true, /*check_exception_and_errno=*/true,
+ /*check_errno=*/true)
+LIST_EXPF_TESTS(DoubleEval, LIBC_NAMESPACE::math::double_eval::expf,
+ /*ulp_tolerance=*/TOLERANCE + 0.5, /*all_rounding=*/true,
+ /*check_exception_and_errno=*/true, /*check_errno=*/true)
+LIST_EXPF_TESTS(FloatEval, LIBC_NAMESPACE::math::float_eval::expf,
+ /*ulp_tolerance=*/1.5, /*all_rounding=*/false,
+ /*check_exception_and_errno=*/true, /*check_errno=*/true)
+LIST_EXPF_TESTS(IntegerEval, LIBC_NAMESPACE::math::integer_eval::expf,
+ /*ulp_tolerance=*/0.5, /*all_rounding=*/false,
+ /*check_exception_and_errno=*/false, /*check_errno=*/false)
+
+static float expf_static_rounding(float x) {
+ return LIBC_NAMESPACE::shared::math::static_rounding::expf(
+ x, LIBC_NAMESPACE::fputil::quick_get_round());
}
-TEST_F(LlvmLibcExpfTest, InFloatRange) {
- constexpr uint32_t COUNT = 1'231;
- constexpr uint32_t STEP = UINT32_MAX / COUNT;
- for (uint32_t i = 0, v = 0; i <= COUNT; ++i, v += STEP) {
- float x = FPBits(v).get_val();
- if (FPBits(v).is_nan() || FPBits(v).is_inf())
- continue;
- libc_errno = 0;
- float result = LIBC_NAMESPACE::expf(x);
-
- // If the computation resulted in an error or did not produce valid result
- // in the single-precision floating point range, then ignore comparing with
- // MPFR result as MPFR can still produce valid results because of its
- // wider precision.
- if (FPBits(result).is_nan() || FPBits(result).is_inf() || libc_errno != 0)
- continue;
- EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Exp, x,
- LIBC_NAMESPACE::expf(x), 0.5);
- }
-}
+LIST_EXPF_TESTS(StaticRounding, expf_static_rounding,
+ /*ulp_tolerance=*/0.5, /*all_rounding=*/true,
+ /*check_exception_and_errno=*/false, /*check_errno=*/false)
diff --git a/libc/test/src/math/smoke/CMakeLists.txt b/libc/test/src/math/smoke/CMakeLists.txt
index 603998cd1c41c..0bfe73a2e643a 100644
--- a/libc/test/src/math/smoke/CMakeLists.txt
+++ b/libc/test/src/math/smoke/CMakeLists.txt
@@ -1436,23 +1436,15 @@ add_fp_unittest(
expf_test.cpp
DEPENDS
libc.hdr.errno_macros
+ libc.hdr.fenv_macros
libc.src.math.expf
- libc.src.__support.FPUtil.fp_bits
-)
-
-add_fp_unittest(
- expf_static_rounding_test
- SUITE
- libc-math-smoke-tests
- SRCS
- expf_static_rounding_test.cpp
- DEPENDS
- libc.hdr.errno_macros
- libc.src.__support.math.expf
+ libc.src.__support.math.expf_double_eval
+ libc.src.__support.math.expf_float_eval
libc.src.__support.math.expf_integer_eval
libc.src.__support.FPUtil.fp_bits
)
+
add_fp_unittest(
expf16_test
SUITE
@@ -1500,7 +1492,10 @@ add_fp_unittest(
exp2f_test.cpp
DEPENDS
libc.hdr.errno_macros
+ libc.hdr.fenv_macros
libc.src.math.exp2f
+ libc.src.__support.math.exp2f_double_eval
+ libc.src.__support.math.exp2f_float_eval
libc.src.__support.FPUtil.fp_bits
)
@@ -1561,7 +1556,10 @@ add_fp_unittest(
exp10f_test.cpp
DEPENDS
libc.hdr.errno_macros
+ libc.hdr.fenv_macros
libc.src.math.exp10f
+ libc.src.__support.math.exp10f_double_eval
+ libc.src.__support.math.exp10f_float_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 8d7c1714bcfd5..00c65dcf6acb9 100644
--- a/libc/test/src/math/smoke/exp10f_test.cpp
+++ b/libc/test/src/math/smoke/exp10f_test.cpp
@@ -1,82 +1,112 @@
-//===-- Unittests for exp10f ----------------------------------------------===//
+//===----------------------------------------------------------------------===//
//
// 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 smoke tests for exp10f.
+///
+//===----------------------------------------------------------------------===//
#include "hdr/errno_macros.h"
+#include "hdr/fenv_macros.h"
#include "hdr/math_macros.h"
#include "hdr/stdint_proxy.h"
#include "src/__support/FPUtil/FPBits.h"
+#include "src/__support/math/exp10f_double_eval.h"
+#include "src/__support/math/exp10f_float_eval.h"
#include "src/math/exp10f.h"
#include "test/UnitTest/FPMatcher.h"
#include "test/UnitTest/Test.h"
-using LlvmLibcExp10fTest = LIBC_NAMESPACE::testing::FPTest<float>;
-
-TEST_F(LlvmLibcExp10fTest, SpecialNumbers) {
- EXPECT_FP_EQ_WITH_EXCEPTION(aNaN, LIBC_NAMESPACE::exp10f(sNaN), FE_INVALID);
- EXPECT_MATH_ERRNO(0);
-
- EXPECT_FP_EQ_ALL_ROUNDING(aNaN, LIBC_NAMESPACE::exp10f(aNaN));
- EXPECT_MATH_ERRNO(0);
-
- EXPECT_FP_EQ_ALL_ROUNDING(inf, LIBC_NAMESPACE::exp10f(inf));
- EXPECT_MATH_ERRNO(0);
-
- EXPECT_FP_EQ_ALL_ROUNDING(0.0f, LIBC_NAMESPACE::exp10f(neg_inf));
- EXPECT_MATH_ERRNO(0);
-
- EXPECT_FP_EQ_ALL_ROUNDING(1.0f, LIBC_NAMESPACE::exp10f(0.0f));
- EXPECT_MATH_ERRNO(0);
-
- EXPECT_FP_EQ_ALL_ROUNDING(1.0f, LIBC_NAMESPACE::exp10f(-0.0f));
- EXPECT_MATH_ERRNO(0);
-
- EXPECT_FP_EQ_ALL_ROUNDING(10.0f, LIBC_NAMESPACE::exp10f(1.0f));
- EXPECT_FP_EQ_ALL_ROUNDING(100.0f, LIBC_NAMESPACE::exp10f(2.0f));
- EXPECT_FP_EQ_ALL_ROUNDING(1000.0f, LIBC_NAMESPACE::exp10f(3.0f));
-}
-
-TEST_F(LlvmLibcExp10fTest, Overflow) {
- EXPECT_FP_EQ_WITH_EXCEPTION(
- inf, LIBC_NAMESPACE::exp10f(FPBits(0x7f7fffffU).get_val()), FE_OVERFLOW);
- EXPECT_MATH_ERRNO(ERANGE);
-
- EXPECT_FP_EQ_WITH_EXCEPTION(
- inf, LIBC_NAMESPACE::exp10f(FPBits(0x43000000U).get_val()), FE_OVERFLOW);
- EXPECT_MATH_ERRNO(ERANGE);
-
- EXPECT_FP_EQ_WITH_EXCEPTION(
- inf, LIBC_NAMESPACE::exp10f(FPBits(0x43000001U).get_val()), FE_OVERFLOW);
- EXPECT_MATH_ERRNO(ERANGE);
-}
+class Exp10fTest : public LIBC_NAMESPACE::testing::FPTest<float> {
+public:
+ void test_special_numbers(float (*func)(float),
+ bool check_snan_invalid = false) {
+ 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);
+
+ EXPECT_FP_EQ_ALL_ROUNDING(aNaN, func(aNaN));
+ EXPECT_MATH_ERRNO(0);
+
+ EXPECT_FP_EQ_ALL_ROUNDING(inf, func(inf));
+ EXPECT_MATH_ERRNO(0);
+
+ EXPECT_FP_EQ_ALL_ROUNDING(0.0f, func(neg_inf));
+ EXPECT_MATH_ERRNO(0);
+
+ EXPECT_FP_EQ_ALL_ROUNDING(1.0f, func(0.0f));
+ EXPECT_MATH_ERRNO(0);
+
+ EXPECT_FP_EQ_ALL_ROUNDING(1.0f, func(-0.0f));
+ EXPECT_MATH_ERRNO(0);
+
+ EXPECT_FP_EQ_ALL_ROUNDING(10.0f, func(1.0f));
+ EXPECT_FP_EQ_ALL_ROUNDING(100.0f, func(2.0f));
+ EXPECT_FP_EQ_ALL_ROUNDING(1000.0f, func(3.0f));
+ }
+
+ void test_overflow(float (*func)(float),
+ bool check_exception_and_errno = true) {
+ constexpr float VALUES[] = {
+ FPBits(0x7f7fffffU).get_val(),
+ FPBits(0x43000000U).get_val(),
+ FPBits(0x43000001U).get_val(),
+ };
+ for (float x : VALUES) {
+ if (check_exception_and_errno) {
+ EXPECT_FP_EQ_WITH_EXCEPTION(inf, func(x), FE_OVERFLOW);
+ EXPECT_MATH_ERRNO(ERANGE);
+ } else {
+ EXPECT_FP_EQ(inf, func(x));
+ }
+ }
+ }
#ifdef LIBC_TEST_FTZ_DAZ
+ void test_denormals(float (*func)(float)) {
+ EXPECT_FP_EQ(1.0f, func(min_denormal));
+ EXPECT_FP_EQ(1.0f, func(max_denormal));
+ }
+#endif // LIBC_TEST_FTZ_DAZ
+};
-using namespace LIBC_NAMESPACE::testing;
-
-TEST_F(LlvmLibcExp10fTest, FTZMode) {
- ModifyMXCSR mxcsr(FTZ);
-
- EXPECT_FP_EQ(1.0f, LIBC_NAMESPACE::exp10f(min_denormal));
- EXPECT_FP_EQ(1.0f, LIBC_NAMESPACE::exp10f(max_denormal));
-}
-
-TEST_F(LlvmLibcExp10fTest, DAZMode) {
- ModifyMXCSR mxcsr(DAZ);
-
- EXPECT_FP_EQ(1.0f, LIBC_NAMESPACE::exp10f(min_denormal));
- EXPECT_FP_EQ(1.0f, LIBC_NAMESPACE::exp10f(max_denormal));
-}
-
-TEST_F(LlvmLibcExp10fTest, FTZDAZMode) {
- ModifyMXCSR mxcsr(FTZ | DAZ);
-
- EXPECT_FP_EQ(1.0f, LIBC_NAMESPACE::exp10f(min_denormal));
- EXPECT_FP_EQ(1.0f, LIBC_NAMESPACE::exp10f(max_denormal));
-}
-
-#endif
+#ifdef LIBC_TEST_FTZ_DAZ
+#define LIST_EXP10F_FTZ_DAZ_TESTS(suffix, func) \
+ TEST_F(LlvmLibcExp10fTest##suffix, FTZMode) { \
+ LIBC_NAMESPACE::testing::ModifyMXCSR mxcsr(LIBC_NAMESPACE::testing::FTZ); \
+ test_denormals(&func); \
+ } \
+ TEST_F(LlvmLibcExp10fTest##suffix, DAZMode) { \
+ LIBC_NAMESPACE::testing::ModifyMXCSR mxcsr(LIBC_NAMESPACE::testing::DAZ); \
+ test_denormals(&func); \
+ } \
+ TEST_F(LlvmLibcExp10fTest##suffix, FTZDAZMode) { \
+ LIBC_NAMESPACE::testing::ModifyMXCSR mxcsr(LIBC_NAMESPACE::testing::FTZ | \
+ LIBC_NAMESPACE::testing::DAZ); \
+ test_denormals(&func); \
+ }
+#else // !LIBC_TEST_FTZ_DAZ
+#define LIST_EXP10F_FTZ_DAZ_TESTS(suffix, func)
+#endif // LIBC_TEST_FTZ_DAZ
+
+#define LIST_EXP10F_TESTS(suffix, func, check_snan_invalid) \
+ using LlvmLibcExp10fTest##suffix = Exp10fTest; \
+ TEST_F(LlvmLibcExp10fTest##suffix, SpecialNumbers) { \
+ test_special_numbers(&func, check_snan_invalid); \
+ } \
+ 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(DoubleEval, LIBC_NAMESPACE::math::double_eval::exp10f,
+ /*check_snan_invalid=*/false)
+LIST_EXP10F_TESTS(FloatEval, LIBC_NAMESPACE::math::float_eval::exp10f,
+ /*check_snan_invalid=*/false)
diff --git a/libc/test/src/math/smoke/exp2f_test.cpp b/libc/test/src/math/smoke/exp2f_test.cpp
index 2adfa5fc9040a..bb64844eb7558 100644
--- a/libc/test/src/math/smoke/exp2f_test.cpp
+++ b/libc/test/src/math/smoke/exp2f_test.cpp
@@ -1,83 +1,113 @@
-//===-- Unittests for exp2f -----------------------------------------------===//
+//===----------------------------------------------------------------------===//
//
// 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 smoke tests for exp2f.
+///
+//===----------------------------------------------------------------------===//
#include "hdr/errno_macros.h"
+#include "hdr/fenv_macros.h"
#include "hdr/math_macros.h"
#include "hdr/stdint_proxy.h"
#include "src/__support/FPUtil/FPBits.h"
+#include "src/__support/math/exp2f_double_eval.h"
+#include "src/__support/math/exp2f_float_eval.h"
#include "src/math/exp2f.h"
#include "test/UnitTest/FPMatcher.h"
#include "test/UnitTest/Test.h"
-using LlvmLibcExp2fTest = LIBC_NAMESPACE::testing::FPTest<float>;
-
-TEST_F(LlvmLibcExp2fTest, SpecialNumbers) {
- EXPECT_FP_EQ_WITH_EXCEPTION(aNaN, LIBC_NAMESPACE::exp2f(sNaN), FE_INVALID);
- EXPECT_MATH_ERRNO(0);
-
- EXPECT_FP_EQ_ALL_ROUNDING(aNaN, LIBC_NAMESPACE::exp2f(aNaN));
- EXPECT_MATH_ERRNO(0);
-
- EXPECT_FP_EQ_ALL_ROUNDING(inf, LIBC_NAMESPACE::exp2f(inf));
- EXPECT_MATH_ERRNO(0);
-
- EXPECT_FP_EQ_ALL_ROUNDING(0.0f, LIBC_NAMESPACE::exp2f(neg_inf));
- EXPECT_MATH_ERRNO(0);
-
- EXPECT_FP_EQ_ALL_ROUNDING(1.0f, LIBC_NAMESPACE::exp2f(0.0f));
- EXPECT_MATH_ERRNO(0);
-
- EXPECT_FP_EQ_ALL_ROUNDING(1.0f, LIBC_NAMESPACE::exp2f(-0.0f));
- EXPECT_MATH_ERRNO(0);
-
- EXPECT_FP_EQ_ALL_ROUNDING(2.0f, LIBC_NAMESPACE::exp2f(1.0f));
- EXPECT_FP_EQ_ALL_ROUNDING(0.5f, LIBC_NAMESPACE::exp2f(-1.0f));
- EXPECT_FP_EQ_ALL_ROUNDING(4.0f, LIBC_NAMESPACE::exp2f(2.0f));
- EXPECT_FP_EQ_ALL_ROUNDING(0.25f, LIBC_NAMESPACE::exp2f(-2.0f));
-}
-
-TEST_F(LlvmLibcExp2fTest, Overflow) {
- EXPECT_FP_EQ_WITH_EXCEPTION(
- inf, LIBC_NAMESPACE::exp2f(FPBits(0x7f7fffffU).get_val()), FE_OVERFLOW);
- EXPECT_MATH_ERRNO(ERANGE);
-
- EXPECT_FP_EQ_WITH_EXCEPTION(
- inf, LIBC_NAMESPACE::exp2f(FPBits(0x43000000U).get_val()), FE_OVERFLOW);
- EXPECT_MATH_ERRNO(ERANGE);
-
- EXPECT_FP_EQ_WITH_EXCEPTION(
- inf, LIBC_NAMESPACE::exp2f(FPBits(0x43000001U).get_val()), FE_OVERFLOW);
- EXPECT_MATH_ERRNO(ERANGE);
-}
+class Exp2fTest : public LIBC_NAMESPACE::testing::FPTest<float> {
+public:
+ void test_special_numbers(float (*func)(float),
+ bool check_snan_invalid = false) {
+ 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);
+
+ EXPECT_FP_EQ_ALL_ROUNDING(aNaN, func(aNaN));
+ EXPECT_MATH_ERRNO(0);
+
+ EXPECT_FP_EQ_ALL_ROUNDING(inf, func(inf));
+ EXPECT_MATH_ERRNO(0);
+
+ EXPECT_FP_EQ_ALL_ROUNDING(0.0f, func(neg_inf));
+ EXPECT_MATH_ERRNO(0);
+
+ EXPECT_FP_EQ_ALL_ROUNDING(1.0f, func(0.0f));
+ EXPECT_MATH_ERRNO(0);
+
+ EXPECT_FP_EQ_ALL_ROUNDING(1.0f, func(-0.0f));
+ EXPECT_MATH_ERRNO(0);
+
+ EXPECT_FP_EQ_ALL_ROUNDING(2.0f, func(1.0f));
+ EXPECT_FP_EQ_ALL_ROUNDING(0.5f, func(-1.0f));
+ EXPECT_FP_EQ_ALL_ROUNDING(4.0f, func(2.0f));
+ EXPECT_FP_EQ_ALL_ROUNDING(0.25f, func(-2.0f));
+ }
+
+ void test_overflow(float (*func)(float),
+ bool check_exception_and_errno = true) {
+ constexpr float VALUES[] = {
+ FPBits(0x7f7fffffU).get_val(),
+ FPBits(0x43000000U).get_val(),
+ FPBits(0x43000001U).get_val(),
+ };
+ for (float x : VALUES) {
+ if (check_exception_and_errno) {
+ EXPECT_FP_EQ_WITH_EXCEPTION(inf, func(x), FE_OVERFLOW);
+ EXPECT_MATH_ERRNO(ERANGE);
+ } else {
+ EXPECT_FP_EQ(inf, func(x));
+ }
+ }
+ }
#ifdef LIBC_TEST_FTZ_DAZ
+ void test_denormals(float (*func)(float)) {
+ EXPECT_FP_EQ(1.0f, func(min_denormal));
+ EXPECT_FP_EQ(1.0f, func(max_denormal));
+ }
+#endif // LIBC_TEST_FTZ_DAZ
+};
-using namespace LIBC_NAMESPACE::testing;
-
-TEST_F(LlvmLibcExp2fTest, FTZMode) {
- ModifyMXCSR mxcsr(FTZ);
-
- EXPECT_FP_EQ(1.0f, LIBC_NAMESPACE::exp2f(min_denormal));
- EXPECT_FP_EQ(1.0f, LIBC_NAMESPACE::exp2f(max_denormal));
-}
-
-TEST_F(LlvmLibcExp2fTest, DAZMode) {
- ModifyMXCSR mxcsr(DAZ);
-
- EXPECT_FP_EQ(1.0f, LIBC_NAMESPACE::exp2f(min_denormal));
- EXPECT_FP_EQ(1.0f, LIBC_NAMESPACE::exp2f(max_denormal));
-}
-
-TEST_F(LlvmLibcExp2fTest, FTZDAZMode) {
- ModifyMXCSR mxcsr(FTZ | DAZ);
-
- EXPECT_FP_EQ(1.0f, LIBC_NAMESPACE::exp2f(min_denormal));
- EXPECT_FP_EQ(1.0f, LIBC_NAMESPACE::exp2f(max_denormal));
-}
-
-#endif
+#ifdef LIBC_TEST_FTZ_DAZ
+#define LIST_EXP2F_FTZ_DAZ_TESTS(suffix, func) \
+ TEST_F(LlvmLibcExp2fTest##suffix, FTZMode) { \
+ LIBC_NAMESPACE::testing::ModifyMXCSR mxcsr(LIBC_NAMESPACE::testing::FTZ); \
+ test_denormals(&func); \
+ } \
+ TEST_F(LlvmLibcExp2fTest##suffix, DAZMode) { \
+ LIBC_NAMESPACE::testing::ModifyMXCSR mxcsr(LIBC_NAMESPACE::testing::DAZ); \
+ test_denormals(&func); \
+ } \
+ TEST_F(LlvmLibcExp2fTest##suffix, FTZDAZMode) { \
+ LIBC_NAMESPACE::testing::ModifyMXCSR mxcsr(LIBC_NAMESPACE::testing::FTZ | \
+ LIBC_NAMESPACE::testing::DAZ); \
+ test_denormals(&func); \
+ }
+#else // !LIBC_TEST_FTZ_DAZ
+#define LIST_EXP2F_FTZ_DAZ_TESTS(suffix, func)
+#endif // LIBC_TEST_FTZ_DAZ
+
+#define LIST_EXP2F_TESTS(suffix, func, check_snan_invalid) \
+ using LlvmLibcExp2fTest##suffix = Exp2fTest; \
+ TEST_F(LlvmLibcExp2fTest##suffix, SpecialNumbers) { \
+ test_special_numbers(&func, check_snan_invalid); \
+ } \
+ 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(DoubleEval, LIBC_NAMESPACE::math::double_eval::exp2f,
+ /*check_snan_invalid=*/false)
+LIST_EXP2F_TESTS(FloatEval, LIBC_NAMESPACE::math::float_eval::exp2f,
+ /*check_snan_invalid=*/false)
diff --git a/libc/test/src/math/smoke/expf_static_rounding_test.cpp b/libc/test/src/math/smoke/expf_static_rounding_test.cpp
deleted file mode 100644
index 908f87714868c..0000000000000
--- a/libc/test/src/math/smoke/expf_static_rounding_test.cpp
+++ /dev/null
@@ -1,62 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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 smoke tests for static_rounding::expf(x)
-///
-//===----------------------------------------------------------------------===//
-
-#include "hdr/errno_macros.h"
-#include "hdr/math_macros.h"
-#include "hdr/stdint_proxy.h"
-#include "src/__support/FPUtil/FPBits.h"
-#include "src/__support/math/expf.h"
-#include "src/__support/math/expf_integer_eval.h"
-#include "test/UnitTest/FPMatcher.h"
-#include "test/UnitTest/Test.h"
-
-using LlvmLibcExpfStaticRoundingTest = LIBC_NAMESPACE::testing::FPTest<float>;
-
-namespace static_rounding = LIBC_NAMESPACE::shared::math::static_rounding;
-namespace math = LIBC_NAMESPACE::math;
-
-TEST_F(LlvmLibcExpfStaticRoundingTest, SpecialNumbers) {
- using LIBC_NAMESPACE::fputil::testing::get_fe_rounding;
-
- constexpr float VALUES[] = {sNaN, aNaN, inf, neg_inf, 0.0f, -0.0f};
-
- for (auto rounding : ROUNDING_MODES) {
- const int fenv_rounding = get_fe_rounding(rounding);
-
- for (auto x : VALUES) {
- EXPECT_FP_EQ_ROUNDING_MODE(
- math::expf(x), static_rounding::expf(x, fenv_rounding), rounding);
- // Statically rounded expf doesn't raise exceptions, but the baseline
- // expf may raise overflow exception.
- // So, we won't check for that here.
- }
- }
-}
-
-TEST_F(LlvmLibcExpfStaticRoundingTest, Overflow) {
- using LIBC_NAMESPACE::fputil::testing::get_fe_rounding;
-
- constexpr float VALUES[] = {FPBits(0x7f7fffffU).get_val(),
- FPBits(0x42cffff8U).get_val(),
- FPBits(0x42d00008U).get_val()};
-
- for (auto rounding : ROUNDING_MODES) {
- const int fenv_rounding = get_fe_rounding(rounding);
-
- for (auto x : VALUES) {
- EXPECT_FP_EQ_ROUNDING_MODE(
- math::expf(x), static_rounding::expf(x, fenv_rounding), rounding);
- // The same reason above, in the SpecialNumbers smoke test suite
- }
- }
-}
diff --git a/libc/test/src/math/smoke/expf_test.cpp b/libc/test/src/math/smoke/expf_test.cpp
index ca5bc59619201..f63d0d039fe41 100644
--- a/libc/test/src/math/smoke/expf_test.cpp
+++ b/libc/test/src/math/smoke/expf_test.cpp
@@ -1,78 +1,134 @@
-//===-- Unittests for expf ------------------------------------------------===//
+//===----------------------------------------------------------------------===//
//
// 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 smoke tests for expf.
+///
+//===----------------------------------------------------------------------===//
#include "hdr/errno_macros.h"
+#include "hdr/fenv_macros.h"
#include "hdr/math_macros.h"
#include "hdr/stdint_proxy.h"
#include "src/__support/FPUtil/FPBits.h"
+#include "src/__support/math/expf_double_eval.h"
+#include "src/__support/math/expf_float_eval.h"
+#include "src/__support/math/expf_integer_eval.h"
#include "src/math/expf.h"
#include "test/UnitTest/FPMatcher.h"
#include "test/UnitTest/Test.h"
-using LlvmLibcExpfTest = LIBC_NAMESPACE::testing::FPTest<float>;
-
-TEST_F(LlvmLibcExpfTest, SpecialNumbers) {
- EXPECT_FP_EQ_WITH_EXCEPTION(aNaN, LIBC_NAMESPACE::expf(sNaN), FE_INVALID);
- EXPECT_MATH_ERRNO(0);
-
- EXPECT_FP_EQ_ALL_ROUNDING(aNaN, LIBC_NAMESPACE::expf(aNaN));
- EXPECT_MATH_ERRNO(0);
-
- EXPECT_FP_EQ_ALL_ROUNDING(inf, LIBC_NAMESPACE::expf(inf));
- EXPECT_MATH_ERRNO(0);
-
- EXPECT_FP_EQ_ALL_ROUNDING(0.0f, LIBC_NAMESPACE::expf(neg_inf));
- EXPECT_MATH_ERRNO(0);
-
- EXPECT_FP_EQ_ALL_ROUNDING(1.0f, LIBC_NAMESPACE::expf(0.0f));
- EXPECT_MATH_ERRNO(0);
-
- EXPECT_FP_EQ_ALL_ROUNDING(1.0f, LIBC_NAMESPACE::expf(-0.0f));
- EXPECT_MATH_ERRNO(0);
-}
-
-TEST_F(LlvmLibcExpfTest, Overflow) {
- EXPECT_FP_EQ_WITH_EXCEPTION(
- inf, LIBC_NAMESPACE::expf(FPBits(0x7f7fffffU).get_val()), FE_OVERFLOW);
- EXPECT_MATH_ERRNO(ERANGE);
-
- EXPECT_FP_EQ_WITH_EXCEPTION(
- inf, LIBC_NAMESPACE::expf(FPBits(0x42cffff8U).get_val()), FE_OVERFLOW);
- EXPECT_MATH_ERRNO(ERANGE);
-
- EXPECT_FP_EQ_WITH_EXCEPTION(
- inf, LIBC_NAMESPACE::expf(FPBits(0x42d00008U).get_val()), FE_OVERFLOW);
- EXPECT_MATH_ERRNO(ERANGE);
-}
+class ExpfTest : public LIBC_NAMESPACE::testing::FPTest<float> {
+public:
+ void test_special_numbers(float (*func)(float),
+ 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));
+ }
+ if (check_errno)
+ EXPECT_MATH_ERRNO(0);
+
+ EXPECT_FP_EQ_ALL_ROUNDING(aNaN, func(aNaN));
+ if (check_errno)
+ EXPECT_MATH_ERRNO(0);
+
+ EXPECT_FP_EQ_ALL_ROUNDING(inf, func(inf));
+ if (check_errno)
+ EXPECT_MATH_ERRNO(0);
+
+ EXPECT_FP_EQ_ALL_ROUNDING(0.0f, func(neg_inf));
+ if (check_errno)
+ EXPECT_MATH_ERRNO(0);
+
+ EXPECT_FP_EQ_ALL_ROUNDING(1.0f, func(0.0f));
+ if (check_errno)
+ EXPECT_MATH_ERRNO(0);
+
+ EXPECT_FP_EQ_ALL_ROUNDING(1.0f, func(-0.0f));
+ if (check_errno)
+ EXPECT_MATH_ERRNO(0);
+ }
+
+ void test_overflow(float (*func)(float),
+ bool check_exception_and_errno = true) {
+ constexpr float VALUES[] = {
+ FPBits(0x7f7fffffU).get_val(),
+ FPBits(0x42cffff8U).get_val(),
+ FPBits(0x42d00008U).get_val(),
+ };
+ for (float x : VALUES) {
+ if (check_exception_and_errno) {
+ EXPECT_FP_EQ_WITH_EXCEPTION(inf, func(x), FE_OVERFLOW);
+ EXPECT_MATH_ERRNO(ERANGE);
+ } else {
+ EXPECT_FP_EQ(inf, func(x));
+ }
+ }
+ }
#ifdef LIBC_TEST_FTZ_DAZ
+ void test_denormals(float (*func)(float)) {
+ EXPECT_FP_EQ(1.0f, func(min_denormal));
+ EXPECT_FP_EQ(1.0f, func(max_denormal));
+ }
+#endif // LIBC_TEST_FTZ_DAZ
+};
-using namespace LIBC_NAMESPACE::testing;
-
-TEST_F(LlvmLibcExpfTest, FTZMode) {
- ModifyMXCSR mxcsr(FTZ);
-
- EXPECT_FP_EQ(1.0f, LIBC_NAMESPACE::expf(min_denormal));
- EXPECT_FP_EQ(1.0f, LIBC_NAMESPACE::expf(max_denormal));
-}
-
-TEST_F(LlvmLibcExpfTest, DAZMode) {
- ModifyMXCSR mxcsr(DAZ);
-
- EXPECT_FP_EQ(1.0f, LIBC_NAMESPACE::expf(min_denormal));
- EXPECT_FP_EQ(1.0f, LIBC_NAMESPACE::expf(max_denormal));
-}
-
-TEST_F(LlvmLibcExpfTest, FTZDAZMode) {
- ModifyMXCSR mxcsr(FTZ | DAZ);
-
- EXPECT_FP_EQ(1.0f, LIBC_NAMESPACE::expf(min_denormal));
- EXPECT_FP_EQ(1.0f, LIBC_NAMESPACE::expf(max_denormal));
+#ifdef LIBC_TEST_FTZ_DAZ
+#define LIST_EXPF_FTZ_DAZ_TESTS(suffix, func) \
+ TEST_F(LlvmLibcExpfTest##suffix, FTZMode) { \
+ LIBC_NAMESPACE::testing::ModifyMXCSR mxcsr(LIBC_NAMESPACE::testing::FTZ); \
+ test_denormals(&func); \
+ } \
+ TEST_F(LlvmLibcExpfTest##suffix, DAZMode) { \
+ LIBC_NAMESPACE::testing::ModifyMXCSR mxcsr(LIBC_NAMESPACE::testing::DAZ); \
+ test_denormals(&func); \
+ } \
+ TEST_F(LlvmLibcExpfTest##suffix, FTZDAZMode) { \
+ LIBC_NAMESPACE::testing::ModifyMXCSR mxcsr(LIBC_NAMESPACE::testing::FTZ | \
+ LIBC_NAMESPACE::testing::DAZ); \
+ test_denormals(&func); \
+ }
+#else // !LIBC_TEST_FTZ_DAZ
+#define LIST_EXPF_FTZ_DAZ_TESTS(suffix, func)
+#endif // LIBC_TEST_FTZ_DAZ
+
+#define LIST_EXPF_TESTS(suffix, func, check_snan_invalid, \
+ check_exception_and_errno, check_errno) \
+ using LlvmLibcExpfTest##suffix = ExpfTest; \
+ TEST_F(LlvmLibcExpfTest##suffix, SpecialNumbers) { \
+ test_special_numbers(&func, check_snan_invalid, check_errno); \
+ } \
+ TEST_F(LlvmLibcExpfTest##suffix, Overflow) { \
+ test_overflow(&func, check_exception_and_errno); \
+ } \
+ LIST_EXPF_FTZ_DAZ_TESTS(suffix, func)
+
+LIST_EXPF_TESTS(Default, LIBC_NAMESPACE::expf, /*check_snan_invalid=*/true,
+ /*check_exception_and_errno=*/true, /*check_errno=*/true)
+LIST_EXPF_TESTS(DoubleEval, LIBC_NAMESPACE::math::double_eval::expf,
+ /*check_snan_invalid=*/false,
+ /*check_exception_and_errno=*/true, /*check_errno=*/true)
+LIST_EXPF_TESTS(FloatEval, LIBC_NAMESPACE::math::float_eval::expf,
+ /*check_snan_invalid=*/false,
+ /*check_exception_and_errno=*/true, /*check_errno=*/true)
+LIST_EXPF_TESTS(IntegerEval, LIBC_NAMESPACE::math::integer_eval::expf,
+ /*check_snan_invalid=*/false,
+ /*check_exception_and_errno=*/false, /*check_errno=*/false)
+
+static float expf_static_rounding(float x) {
+ return LIBC_NAMESPACE::shared::math::static_rounding::expf(
+ x, LIBC_NAMESPACE::fputil::quick_get_round());
}
-#endif
+LIST_EXPF_TESTS(StaticRounding, expf_static_rounding,
+ /*check_snan_invalid=*/false,
+ /*check_exception_and_errno=*/false, /*check_errno=*/false)
>From ab0b77c8c03fb3bde8031ae3c5afcbd2bcfb9d8e Mon Sep 17 00:00:00 2001
From: Tue Ly <lntue.h at gmail.com>
Date: Sat, 19 Sep 2026 02:40:43 +0000
Subject: [PATCH 2/3] Remove redundant typename.
---
libc/src/__support/math/exp10f_double_eval.h | 2 +-
libc/src/__support/math/exp10f_float_eval.h | 2 +-
libc/src/__support/math/exp2f_double_eval.h | 2 +-
libc/src/__support/math/exp2f_float_eval.h | 2 +-
libc/src/__support/math/expf_double_eval.h | 2 +-
libc/src/__support/math/expf_float_eval.h | 2 +-
libc/src/__support/math/expf_integer_eval.h | 2 +-
7 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/libc/src/__support/math/exp10f_double_eval.h b/libc/src/__support/math/exp10f_double_eval.h
index 03e3790c87e70..e1a7cc9d68e98 100644
--- a/libc/src/__support/math/exp10f_double_eval.h
+++ b/libc/src/__support/math/exp10f_double_eval.h
@@ -28,7 +28,7 @@ namespace math {
namespace double_eval {
LIBC_INLINE float exp10f(float x) {
- using FPBits = typename fputil::FPBits<float>;
+ using FPBits = fputil::FPBits<float>;
FPBits xbits(x);
uint32_t x_u = xbits.uintval();
diff --git a/libc/src/__support/math/exp10f_float_eval.h b/libc/src/__support/math/exp10f_float_eval.h
index 19bbfb93a509e..304d3e310234c 100644
--- a/libc/src/__support/math/exp10f_float_eval.h
+++ b/libc/src/__support/math/exp10f_float_eval.h
@@ -31,7 +31,7 @@ namespace math {
namespace float_eval {
LIBC_INLINE float exp10f(float x) {
- using FPBits = typename fputil::FPBits<float>;
+ using FPBits = fputil::FPBits<float>;
FPBits xbits(x);
uint32_t x_u = xbits.uintval();
diff --git a/libc/src/__support/math/exp2f_double_eval.h b/libc/src/__support/math/exp2f_double_eval.h
index 821e726d012d3..e0387f32e193f 100644
--- a/libc/src/__support/math/exp2f_double_eval.h
+++ b/libc/src/__support/math/exp2f_double_eval.h
@@ -32,7 +32,7 @@ namespace math {
namespace double_eval {
LIBC_INLINE float exp2f(float x) {
- using FPBits = typename fputil::FPBits<float>;
+ using FPBits = fputil::FPBits<float>;
FPBits xbits(x);
uint32_t x_u = xbits.uintval();
diff --git a/libc/src/__support/math/exp2f_float_eval.h b/libc/src/__support/math/exp2f_float_eval.h
index 8298ae7b18add..d1ea060478f6f 100644
--- a/libc/src/__support/math/exp2f_float_eval.h
+++ b/libc/src/__support/math/exp2f_float_eval.h
@@ -29,7 +29,7 @@ namespace math {
namespace float_eval {
LIBC_INLINE float exp2f(float x) {
- using FPBits = typename fputil::FPBits<float>;
+ using FPBits = fputil::FPBits<float>;
FPBits xbits(x);
uint32_t x_u = xbits.uintval();
diff --git a/libc/src/__support/math/expf_double_eval.h b/libc/src/__support/math/expf_double_eval.h
index 8a990bd67c265..12f745a4bc60a 100644
--- a/libc/src/__support/math/expf_double_eval.h
+++ b/libc/src/__support/math/expf_double_eval.h
@@ -30,7 +30,7 @@ namespace math {
namespace double_eval {
LIBC_INLINE float expf(float x) {
- using FPBits = typename fputil::FPBits<float>;
+ using FPBits = fputil::FPBits<float>;
FPBits xbits(x);
uint32_t x_u = xbits.uintval();
diff --git a/libc/src/__support/math/expf_float_eval.h b/libc/src/__support/math/expf_float_eval.h
index 5561e9d8e06f8..e479cb58d2fea 100644
--- a/libc/src/__support/math/expf_float_eval.h
+++ b/libc/src/__support/math/expf_float_eval.h
@@ -31,7 +31,7 @@ namespace math {
namespace float_eval {
LIBC_INLINE float expf(float x) {
- using FPBits = typename fputil::FPBits<float>;
+ using FPBits = fputil::FPBits<float>;
FPBits xbits(x);
uint32_t x_u = xbits.uintval();
diff --git a/libc/src/__support/math/expf_integer_eval.h b/libc/src/__support/math/expf_integer_eval.h
index c072abaf96955..52736323d8d7e 100644
--- a/libc/src/__support/math/expf_integer_eval.h
+++ b/libc/src/__support/math/expf_integer_eval.h
@@ -59,7 +59,7 @@ LIBC_INLINE_VAR constexpr Frac64 EXPF_COEFFS[] = {
// Statically rounded, no except implementation of expf using integer-only
// arithmetic.
LIBC_INLINE float expf(float x, [[maybe_unused]] int rounding) {
- using FPBits = typename fputil::FPBits<float>;
+ using FPBits = fputil::FPBits<float>;
using FPBounds = LIBC_NAMESPACE::math::check::exp_internal::Bounds<float>;
FPBits xbits(x);
>From b5a7c89118bc3d4439211cf487959fb2a6917ff0 Mon Sep 17 00:00:00 2001
From: lntue <lntue.h at gmail.com>
Date: Sat, 19 Sep 2026 09:14:24 -0400
Subject: [PATCH 3/3] Update libc/test/src/math/exhaustive/CMakeLists.txt
Co-authored-by: Muhammad Bassiouni <60100307+bassiounix at users.noreply.github.com>
---
libc/test/src/math/exhaustive/CMakeLists.txt | 1 -
1 file changed, 1 deletion(-)
diff --git a/libc/test/src/math/exhaustive/CMakeLists.txt b/libc/test/src/math/exhaustive/CMakeLists.txt
index 2d42bee4c6540..23a66e28cf17f 100644
--- a/libc/test/src/math/exhaustive/CMakeLists.txt
+++ b/libc/test/src/math/exhaustive/CMakeLists.txt
@@ -710,7 +710,6 @@ add_fp_unittest(
libc.src.__support.math.atanf
)
-
add_fp_unittest(
asinf_test
NO_RUN_POSTBUILD
More information about the libc-commits
mailing list