[libc-commits] [libc] 9dc80b9 - [libc][math] Add correctly rounded, float-only implementation of expf for GPUs (#231671)
via libc-commits
libc-commits at lists.llvm.org
Thu Oct 15 09:51:05 PDT 2026
Author: Leandro Lacerda
Date: 2026-10-15T12:50:57-04:00
New Revision: 9dc80b90f8112ee0d371c46f65c12b012d453196
URL: https://github.com/llvm/llvm-project/commit/9dc80b90f8112ee0d371c46f65c12b012d453196
DIFF: https://github.com/llvm/llvm-project/commit/9dc80b90f8112ee0d371c46f65c12b012d453196.diff
LOG: [libc][math] Add correctly rounded, float-only implementation of expf for GPUs (#231671)
This patch adds a GPU-oriented implementation of `expf`, correctly
rounded for all inputs in round-to-nearest, ties-to-even mode. It uses
float-only intermediate arithmetic and Horner evaluation, without lookup
tables, a Ziv test, or separate fast and accurate paths.
It requires single-precision FMA.
### Performance on NVIDIA GeForce RTX 5070 (Cycles/Call)
Results were collected using a locally adapted `expf` benchmark based on
the GPU benchmark infrastructure in `libc/benchmarks/gpu`.
Tables report median cycles/call across five per-run means, with a
per-thread batch size of `N = 128`. Lower is better; the lowest value in
each row is bold. `Relative` compares FloatEvalCrRn (this PR) with the
fastest alternative in each row: “faster” means fewer cycles/call;
“slower” means more.
**Correctly rounded implementations in round-to-nearest, ties-to-even**
Both implementations are correctly rounded in this mode under the build
configuration below.
| Domain | DoubleEval | FloatEvalCrRn | Relative |
|---|---:|---:|---|
| CoreRange `[-10.0, 10.0]` | 532 | **212** | 2.51x faster |
| Normal `[-87.3, 88.7]` | 540 | **212** | 2.55x faster |
| Subnormal `[-103.0, -87.5]` | 626 | **212** | 2.95x faster |
| Finite `[-103.0, 88.7]` | 627 | **212** | 2.96x faster |
| Underflow `[-104.0, -103.0]` | 650 | **212** | 3.07x faster |
| Overflow `[88.0, 89.0]` | 532 | **212** | 2.51x faster |
**Float-only implementations**
Both use float-only intermediate arithmetic. FloatEval has no
correct-rounding guarantee; FloatEvalCrRn is correctly rounded in
round-to-nearest, ties-to-even.
| Domain | FloatEval | FloatEvalCrRn | Relative |
|---|---:|---:|---|
| CoreRange `[-10.0, 10.0]` | **175** | 212 | 1.21x slower |
| Normal `[-87.3, 88.7]` | **177** | 212 | 1.20x slower |
| Subnormal `[-103.0, -87.5]` | 295 | **212** | 1.39x faster |
| Finite `[-103.0, 88.7]` | 305 | **212** | 1.44x faster |
| Underflow `[-104.0, -103.0]` | 310 | **212** | 1.46x faster |
| Overflow `[88.0, 89.0]` | 222 | **212** | 1.05x faster |
**GPU-oriented, correctly rounded implementations in round-to-nearest,
ties-to-even**
All three use float-only intermediate arithmetic.
CoreMath128 and CoreMath1024 are Clang adaptations of the CUDA-enabled
variants from the CORE-MATH project
[[link](https://gitlab.inria.fr/core-math/core-math/-/blob/as_expf_floatonly/src/binary32/exp/expf.c)].
Their main lookup tables contain 128 and 1024 entries, respectively.
Both use a fast path with a Ziv test and an accurate path when the test
is inconclusive; both paths were enabled during benchmarking.
FloatEvalCrRn uses a single approximation path, without lookup tables or
a Ziv test.
| Domain | CoreMath128 | CoreMath1024 | FloatEvalCrRn | Relative |
|---|---:|---:|---:|---|
| CoreRange `[-10.0, 10.0]` | 251 | 364 | **212** | 1.18x faster |
| Normal `[-87.3, 88.7]` | 253 | 362 | **212** | 1.19x faster |
| Subnormal `[-103.0, -87.5]` | 254 | 367 | **212** | 1.20x faster |
| Finite `[-103.0, 88.7]` | 303 | 474 | **212** | 1.43x faster |
| Underflow `[-104.0, -103.0]` | 295 | 415 | **212** | 1.39x faster |
| Overflow `[88.0, 89.0]` | 318 | 444 | **212** | 1.50x faster |
**Environment and build configuration**
- GPU: NVIDIA GeForce RTX 5070, Blackwell, compute capability 12.0
(`sm_120`).
- CUDA Toolkit: 13.4 (`ptxas` 13.4.92).
- NVIDIA driver: 595.91.07.
- Compiler: Clang 24.0.0git, revision
`2d67f8878f55b5bcc6685f177aaa970c9dc7053a`.
- Compilation: `-O3`, LTO (`-flto`), `-march=sm_120`.
- Math configuration in `libc/config/gpu/config.json`:
`(LIBC_MATH_ASSUME_ROUND_NEAREST_ONLY |
LIBC_MATH_INTERMEDIATE_COMP_IN_FLOAT | LIBC_MATH_NO_ERRNO |
LIBC_MATH_NO_EXCEPT)`
---------
Assisted-by: Codex was used for performance analysis, code review, and
improvements to code comments.
---------
Signed-off-by: Leandro Augusto Lacerda Campos <leandrolcampos at yahoo.com.br>
Added:
libc/src/__support/math/expf_float_utils.h
Modified:
libc/benchmarks/gpu/CMakeLists.txt
libc/src/__support/math/CMakeLists.txt
libc/src/__support/math/expf.h
libc/src/__support/math/expf_float_eval.h
libc/test/src/math/CMakeLists.txt
libc/test/src/math/exhaustive/expf_test.cpp
libc/test/src/math/expf_test.cpp
libc/test/src/math/smoke/CMakeLists.txt
libc/test/src/math/smoke/expf_test.cpp
Removed:
################################################################################
diff --git a/libc/benchmarks/gpu/CMakeLists.txt b/libc/benchmarks/gpu/CMakeLists.txt
index c31a69c59db1c4..e7ad4f59bf7180 100644
--- a/libc/benchmarks/gpu/CMakeLists.txt
+++ b/libc/benchmarks/gpu/CMakeLists.txt
@@ -58,7 +58,7 @@ function(add_benchmark_framework_library name)
target_compile_definitions(${name} PRIVATE TARGET_SUPPORTS_CLOCK)
endif()
- _get_hermetic_test_compile_options(compile_options "")
+ _get_hermetic_test_compile_options(compile_options "" "")
target_include_directories(${name} PRIVATE ${LIBC_INCLUDE_DIR})
target_compile_options(${name} PRIVATE ${compile_options} -nostdinc++)
diff --git a/libc/src/__support/math/CMakeLists.txt b/libc/src/__support/math/CMakeLists.txt
index ef33e3f8ada7b0..d1ad4c9a76391c 100644
--- a/libc/src/__support/math/CMakeLists.txt
+++ b/libc/src/__support/math/CMakeLists.txt
@@ -3126,12 +3126,26 @@ add_header_library(
libc.src.__support.macros.optimization
)
+add_header_library(
+ expf_float_utils
+ HDRS
+ expf_float_utils.h
+ DEPENDS
+ libc.src.__support.FPUtil.double_double
+ libc.src.__support.FPUtil.fp_bits
+ libc.src.__support.FPUtil.multiply_add
+ libc.src.__support.FPUtil.polyeval
+ libc.src.__support.common
+ libc.src.__support.macros.config
+)
+
add_header_library(
expf_float_eval
HDRS
expf_float_eval.h
DEPENDS
.exp2f_float_utils
+ .expf_float_utils
libc.src.__support.FPUtil.double_double
libc.src.__support.FPUtil.fenv_impl
libc.src.__support.FPUtil.fp_bits
diff --git a/libc/src/__support/math/expf.h b/libc/src/__support/math/expf.h
index a64b323e08b2db..d53d19cfc78475 100644
--- a/libc/src/__support/math/expf.h
+++ b/libc/src/__support/math/expf.h
@@ -19,30 +19,41 @@
#include "src/__support/macros/optimization.h"
#include "src/__support/macros/properties/cpu_features.h"
-#if defined(LIBC_MATH_HAS_SKIP_ACCURATE_PASS) && \
+#if defined(LIBC_TARGET_ARCH_IS_GPU) && \
+ !defined(LIBC_MATH_HAS_SKIP_ACCURATE_PASS) && \
+ (defined(LIBC_MATH_HAS_SMALL_TABLES) || \
+ defined(LIBC_MATH_HAS_INTERMEDIATE_COMP_IN_FLOAT)) && \
+ defined(LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY) && \
+ defined(LIBC_MATH_HAS_NO_EXCEPT) && defined(LIBC_MATH_HAS_NO_ERRNO)
+
+#include "src/__support/math/expf_float_eval.h"
+#define LIBC_MATH_EXPF_IMPL float_eval::cr_expf_rn
+
+#elif defined(LIBC_MATH_HAS_SKIP_ACCURATE_PASS) && \
(defined(LIBC_MATH_HAS_SMALL_TABLES) || \
defined(LIBC_MATH_HAS_INTERMEDIATE_COMP_IN_FLOAT))
#include "src/__support/math/expf_float_eval.h"
-#define LIBC_MATH_EXPF_IMPL float_eval
+#define LIBC_MATH_EXPF_IMPL float_eval::expf
#elif !defined(LIBC_TARGET_CPU_HAS_FPU_DOUBLE) && \
defined(LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY) && \
defined(LIBC_MATH_HAS_NO_EXCEPT) && defined(LIBC_MATH_HAS_NO_ERRNO)
#include "src/__support/math/expf_integer_eval.h"
-#define LIBC_MATH_EXPF_IMPL integer_eval
+#define LIBC_MATH_EXPF_IMPL integer_eval::expf
#else
+
#include "src/__support/math/expf_double_eval.h"
-#define LIBC_MATH_EXPF_IMPL double_eval
+#define LIBC_MATH_EXPF_IMPL double_eval::expf
-#endif // LIBC_MATH_HAS_SKIP_ACCURATE_PASS
+#endif
namespace LIBC_NAMESPACE_DECL {
namespace math {
-using LIBC_MATH_EXPF_IMPL::expf;
+LIBC_INLINE float expf(float x) { return LIBC_MATH_EXPF_IMPL(x); }
} // namespace math
} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/__support/math/expf_float_eval.h b/libc/src/__support/math/expf_float_eval.h
index ab8ad649c539ca..ed7aabfd8f0d29 100644
--- a/libc/src/__support/math/expf_float_eval.h
+++ b/libc/src/__support/math/expf_float_eval.h
@@ -7,7 +7,7 @@
//===----------------------------------------------------------------------===//
///
/// \file
-/// Float-only implementation of expf.
+/// Float-only implementations of expf.
///
//===----------------------------------------------------------------------===//
@@ -25,6 +25,7 @@
#include "src/__support/macros/optimization.h"
#include "src/__support/macros/properties/cpu_features.h"
#include "src/__support/math/exp2f_float_utils.h"
+#include "src/__support/math/expf_float_utils.h"
namespace LIBC_NAMESPACE_DECL {
namespace math {
@@ -119,6 +120,75 @@ LIBC_INLINE float expf(float x) {
return exp2f_eval(u, k);
}
+// Designed for GPU performance. Correctly rounded only in round-to-nearest,
+// ties-to-even mode. Requires single-precision FMA. Does not support errno
+// or floating-point exception reporting.
+LIBC_INLINE float cr_expf_rn(float x) {
+ using FPBits = fputil::FPBits<float>;
+ FPBits x_bits(x);
+
+ uint32_t x_u = x_bits.uintval();
+ uint32_t x_abs_u = x_u & 0x7fff'ffffU;
+
+ // |x| <= 2^-25
+ bool is_tiny = x_abs_u <= 0x3300'0000U;
+
+ // x > log(2^128) or x is nan
+ bool should_overflow = x_u > 0x42b1'7217U && x_u < 0x8000'0000U;
+
+ // x <= log(2^-150) or x is nan
+ bool should_underflow = x_u >= 0xc2cf'f1b5U;
+
+ // x is nan with its sign bit set
+ bool is_neg_nan = should_underflow && x_bits.is_nan();
+
+ // log(2^-150) < x < log(2^-126)
+ bool produces_subnormal = x_u >= 0xc2ae'ac50U && !should_underflow;
+
+ // Use x_safe for range reduction to keep the conversion to int defined
+ float x_safe = (should_underflow || should_overflow) ? 0.0f : x;
+
+ // Range reduction:
+ // k = round(x * log2(e))
+ // x * log2(e) = k + u * log2(e)
+ // e^x = 2^k * e^u
+ //
+ // Constants generated by Sollya with:
+ // > display = hexadecimal!;
+ // > LOG2_E = round(log2(exp(1)), SG, RN);
+ // > LOG_2_HI = round(log(2), SG, RN);
+ // > LOG_2_LO.hi = round(log(2) - LOG_2_HI, SG, RN);
+ // > LOG_2_LO.lo = round(log(2) - LOG_2_HI - LOG_2_LO.hi, SG, RN);
+ constexpr float LOG2_E = 0x1.715476p+0f;
+ constexpr float LOG_2_HI = 0x1.62e43p-1f;
+ constexpr fputil::FloatFloat LOG_2_LO = {/*lo=*/-0x1.950d88p-54f,
+ /*hi=*/-0x1.05c61p-29f};
+
+ float kf = fputil::nearest_integer(x_safe * LOG2_E);
+ int k = static_cast<int>(kf);
+
+ float u_hi = fputil::multiply_add(-kf, LOG_2_HI, x_safe);
+ fputil::FloatFloat u_lo = fputil::quick_mult(-kf, LOG_2_LO);
+
+ fputil::FloatFloat u = fputil::exact_add(u_hi, u_lo.hi);
+ u.lo += u_lo.lo;
+
+ // Polynomial approximation: e^u
+ fputil::FloatFloat exp_u = cr_expf_rn_internal::exp_eval(u);
+
+ // Reconstruction: 2^k * e^u
+ float res = cr_expf_rn_internal::scale_result(exp_u, k, produces_subnormal);
+
+ // Special cases
+ constexpr float INF = FPBits::inf().get_val();
+
+ res = is_tiny ? 1.0f : res;
+ res = should_overflow ? x + INF : res;
+ res = should_underflow ? (is_neg_nan ? x : 0.0f) : res;
+
+ return res;
+}
+
} // namespace float_eval
} // namespace math
} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/__support/math/expf_float_utils.h b/libc/src/__support/math/expf_float_utils.h
new file mode 100644
index 00000000000000..d65abda27af380
--- /dev/null
+++ b/libc/src/__support/math/expf_float_utils.h
@@ -0,0 +1,149 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+/// Utilities for float-only implementations of expf.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC___SUPPORT_MATH_EXPF_FLOAT_UTILS_H
+#define LLVM_LIBC_SRC___SUPPORT_MATH_EXPF_FLOAT_UTILS_H
+
+#include "src/__support/FPUtil/FPBits.h"
+#include "src/__support/FPUtil/PolyEval.h"
+#include "src/__support/FPUtil/double_double.h"
+#include "src/__support/FPUtil/multiply_add.h"
+#include "src/__support/common.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+namespace math {
+namespace float_eval {
+
+namespace cr_expf_rn_internal {
+
+using fputil::FloatFloat;
+
+LIBC_ALWAYS_INLINE FloatFloat horner_step(float x, const FloatFloat &poly,
+ const FloatFloat &coeff) {
+ float hi = fputil::multiply_add(x, poly.hi, coeff.hi);
+ float lo = fputil::multiply_add(x, poly.hi, coeff.hi - hi);
+ lo = fputil::multiply_add(x, poly.lo, lo + coeff.lo);
+ return {lo, hi};
+}
+
+// Approximates exp(u) on [-log(2)/2, log(2)/2] with the degree-10
+// polynomial P(u) = 1 + u + u^2 * Q(u) using Horner evaluation.
+//
+// Coefficients generated by Sollya with:
+// > prec = 165!;
+// > display = hexadecimal!;
+// > p = fpminimax(exp(x), [|2, ..., 10|],
+// [|49, 49, 49, 49, 49, 24, 24, 24, 24|],
+// [-log(2)/2; log(2)/2], relative,
+// floating, 1 + x);
+//
+// Float-float coefficients are split into:
+// > hi = round(coeff(p, i), SG, RN);
+// > lo = round(coeff(p, i) - hi, SG, RN);
+LIBC_INLINE FloatFloat exp_eval(const FloatFloat &u) {
+ // Coefficients of u^2, ..., u^6, in {lo, hi} order.
+ constexpr FloatFloat COEFFS[] = {
+ {-0x1.dp-46f, 0x1p-1f},
+ {-0x1.555edcp-28f, 0x1.555556p-3f},
+ {-0x1.54c28ep-30f, 0x1.555556p-5f},
+ {-0x1.b504e8p-32f, 0x1.111112p-7f},
+ {0x1.158eb2p-35f, 0x1.6c16cp-10f},
+ };
+
+ // The float-only tail contributes to degrees 7 through 10 of P.
+ float tail = fputil::polyeval(u.hi, 0x1.a0198ep-13f, 0x1.a01c0ap-16f,
+ 0x1.72e878p-19f, 0x1.2694cap-22f);
+ FloatFloat poly{0.0f, tail};
+ for (int i = 4; i >= 0; --i)
+ poly = horner_step(u.hi, poly, COEFFS[i]);
+
+ // Form expm1(u.hi) before adding 1, retaining its low component.
+ poly = horner_step(u.hi, poly, FloatFloat{0.0f, 1.0f});
+ FloatFloat expm1_u_hi = fputil::quick_mult(u.hi, poly);
+ FloatFloat exp_u = fputil::exact_add(1.0f, expm1_u_hi.hi);
+
+ // exp(u.hi + u.lo) = exp(u.hi) + u.lo * exp(u.hi) + O(u.lo^2).
+ //
+ // Combine the retained low part of expm1(u.hi) with the correction
+ // u.lo * exp(u.hi), approximating exp(u.hi) by exp_u.hi.
+ float corr = fputil::multiply_add(u.lo, exp_u.hi, expm1_u_hi.lo);
+
+ // Rounding low sums, exp_u.lo + corr, to nearest can create a midpoint for
+ // exp_u.hi + RN(exp_u.lo + corr).
+ //
+ // Round inexact low sums to odd whenever their rounded value is a power of
+ // two. This covers all such midpoint cases in this evaluation and preserves
+ // which side of the midpoint the exact sum lies on.
+ //
+ // FastTwoSum here is exact in RN: for nonzero corr, exp_u.lo is an integer
+ // multiple of ulp(corr), as required by Theorem 1 of "FastTwoSum revisited".
+ FloatFloat lo_sum = fputil::exact_add(exp_u.lo, corr);
+
+ uint32_t lo_sum_u = fputil::FPBits<float>(lo_sum.hi).uintval();
+ bool should_round_to_odd =
+ (lo_sum_u & fputil::FPBits<float>::FRACTION_MASK) == 0 &&
+ lo_sum.lo != 0.0f;
+ uint32_t rounding_adj =
+ ((lo_sum.hi > 0.0f) == (lo_sum.lo > 0.0f)) ? 1U : uint32_t(-1);
+ lo_sum_u += should_round_to_odd ? rounding_adj : 0U;
+ exp_u.lo = fputil::FPBits<float>(lo_sum_u).get_val();
+
+ return exp_u;
+}
+
+// Computes 2^k * (exp_u.hi + exp_u.lo).
+LIBC_INLINE float scale_result(const FloatFloat &exp_u, int k,
+ bool produces_subnormal) {
+ using FPBits = fputil::FPBits<float>;
+
+ // Scaling the rounded sum by 2^k is exact for normal results: only the
+ // exponent changes. For subnormal results, the fixed spacing 2^-149 is
+ // coarser than the scaled spacing of the rounded sum. Scaling can then
+ // discard significand bits and round again.
+ //
+ // Choose B = 2^(-126 - k), so ulp(B) * 2^k = 2^-149. For inputs producing
+ // subnormals, B + exp_u.hi + exp_u.lo lies in B's binade [B, 2B). Rounding
+ // this biased sum therefore uses the desired spacing while staying normal.
+ // Use B = 0 for normal results to share the same arithmetic path.
+ uint32_t bias_u = produces_subnormal ? (1U - static_cast<uint32_t>(k))
+ << FPBits::FRACTION_LEN
+ : 0U;
+ float bias = FPBits(bias_u).get_val();
+
+ // For inputs producing subnormals, B >= exp_u.hi, so FastTwoSum retains
+ // the rounding residual exactly in RN. It is also exact when B = 0,
+ // reducing the final addition to RN(exp_u.hi + exp_u.lo).
+ FloatFloat sum_biased = fputil::exact_add(bias, exp_u.hi);
+ float sum_rounded = sum_biased.hi + (sum_biased.lo + exp_u.lo);
+ uint32_t res_u = FPBits(sum_rounded).uintval();
+
+ // For subnormal results, sum_rounded lies in B's binade [B, 2B):
+ // sum_rounded = B + M * ulp(B), where M is the integer encoded by
+ // its fraction field. Clearing the exponent makes the same fraction
+ // bits represent M * 2^-149 = (sum_rounded - B) * 2^k. This removes
+ // the bias and applies the scale without further rounding.
+ uint32_t res_mask = produces_subnormal ? FPBits::FRACTION_MASK : ~0U;
+ uint32_t exponent_adj = produces_subnormal ? 0U
+ : static_cast<uint32_t>(k)
+ << FPBits::FRACTION_LEN;
+ return FPBits((res_u & res_mask) + exponent_adj).get_val();
+}
+
+} // namespace cr_expf_rn_internal
+
+} // namespace float_eval
+} // namespace math
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC___SUPPORT_MATH_EXPF_FLOAT_UTILS_H
diff --git a/libc/test/src/math/CMakeLists.txt b/libc/test/src/math/CMakeLists.txt
index 92d599fba947e5..f33a9a3f109327 100644
--- a/libc/test/src/math/CMakeLists.txt
+++ b/libc/test/src/math/CMakeLists.txt
@@ -1345,7 +1345,6 @@ add_fp_unittest(
libc.src.__support.FPUtil.fp_bits
)
-
add_fp_unittest(
expf16_test
NEED_MPFR
diff --git a/libc/test/src/math/exhaustive/expf_test.cpp b/libc/test/src/math/exhaustive/expf_test.cpp
index 27aafd7d9ce7aa..c40029a79bdd76 100644
--- a/libc/test/src/math/exhaustive/expf_test.cpp
+++ b/libc/test/src/math/exhaustive/expf_test.cpp
@@ -54,6 +54,27 @@ TEST_F(LlvmLibcExpfFloatExhaustiveTest, NegativeRange) {
test_full_range(mpfr::RoundingMode::Nearest, NEG_START, NEG_STOP);
}
+#ifdef LIBC_TARGET_CPU_HAS_FMA_FLOAT
+// Float-eval implementation correctly rounded only in round-to-nearest,
+// ties-to-even mode: tested against the double-eval version in that mode
+// with a 0 ULP bound.
+static float cr_expf_rn(float x) {
+ return LIBC_NAMESPACE::math::float_eval::cr_expf_rn(x);
+}
+
+using LlvmLibcExpfFloatCrRnExhaustiveTest =
+ LlvmLibcUnaryOpAgainstBaselineExhaustiveMathTest<
+ float, LIBC_NAMESPACE::math::double_eval::expf, cr_expf_rn, 0>;
+
+TEST_F(LlvmLibcExpfFloatCrRnExhaustiveTest, PositiveRange) {
+ test_full_range(mpfr::RoundingMode::Nearest, POS_START, POS_STOP);
+}
+
+TEST_F(LlvmLibcExpfFloatCrRnExhaustiveTest, NegativeRange) {
+ test_full_range(mpfr::RoundingMode::Nearest, NEG_START, NEG_STOP);
+}
+#endif // LIBC_TARGET_CPU_HAS_FMA_FLOAT
+
// Statically rounded implementation: tested against double_eval across all
// roundings.
using LlvmLibcExpfStaticRoundingExhaustiveTest =
diff --git a/libc/test/src/math/expf_test.cpp b/libc/test/src/math/expf_test.cpp
index 2cf30559adabb7..eb2e1d4ea64614 100644
--- a/libc/test/src/math/expf_test.cpp
+++ b/libc/test/src/math/expf_test.cpp
@@ -180,6 +180,11 @@ LIST_EXPF_TESTS(DoubleEval, LIBC_NAMESPACE::math::double_eval::expf,
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)
+#ifdef LIBC_TARGET_CPU_HAS_FMA_FLOAT
+LIST_EXPF_TESTS(FloatEvalCrRn, LIBC_NAMESPACE::math::float_eval::cr_expf_rn,
+ /*ulp_tolerance=*/TOLERANCE + 0.5, /*all_rounding=*/false,
+ /*check_exception_and_errno=*/false, /*check_errno=*/false)
+#endif // LIBC_TARGET_CPU_HAS_FMA_FLOAT
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)
diff --git a/libc/test/src/math/smoke/CMakeLists.txt b/libc/test/src/math/smoke/CMakeLists.txt
index fe9fafdfab9a3f..c00776e8e441ff 100644
--- a/libc/test/src/math/smoke/CMakeLists.txt
+++ b/libc/test/src/math/smoke/CMakeLists.txt
@@ -1444,7 +1444,6 @@ add_fp_unittest(
libc.src.__support.FPUtil.fp_bits
)
-
add_fp_unittest(
expf16_test
SUITE
diff --git a/libc/test/src/math/smoke/expf_test.cpp b/libc/test/src/math/smoke/expf_test.cpp
index f63d0d039fe414..df0e8767c95283 100644
--- a/libc/test/src/math/smoke/expf_test.cpp
+++ b/libc/test/src/math/smoke/expf_test.cpp
@@ -120,6 +120,11 @@ LIST_EXPF_TESTS(DoubleEval, LIBC_NAMESPACE::math::double_eval::expf,
LIST_EXPF_TESTS(FloatEval, LIBC_NAMESPACE::math::float_eval::expf,
/*check_snan_invalid=*/false,
/*check_exception_and_errno=*/true, /*check_errno=*/true)
+#ifdef LIBC_TARGET_CPU_HAS_FMA_FLOAT
+LIST_EXPF_TESTS(FloatEvalCrRn, LIBC_NAMESPACE::math::float_eval::cr_expf_rn,
+ /*check_snan_invalid=*/false,
+ /*check_exception_and_errno=*/false, /*check_errno=*/false)
+#endif // LIBC_TARGET_CPU_HAS_FMA_FLOAT
LIST_EXPF_TESTS(IntegerEval, LIBC_NAMESPACE::math::integer_eval::expf,
/*check_snan_invalid=*/false,
/*check_exception_and_errno=*/false, /*check_errno=*/false)
More information about the libc-commits
mailing list