[libc-commits] [libc] 4190d57 - [APFloat] Add exp function for APFloat::IEEESsingle using expf implementation from LLVM libc. (#143959)

via libc-commits libc-commits at lists.llvm.org
Mon Dec 15 07:21:52 PST 2025


Author: lntue
Date: 2025-12-15T10:21:45-05:00
New Revision: 4190d576823c18f45ee0632baee7d798448178ac

URL: https://github.com/llvm/llvm-project/commit/4190d576823c18f45ee0632baee7d798448178ac
DIFF: https://github.com/llvm/llvm-project/commit/4190d576823c18f45ee0632baee7d798448178ac.diff

LOG: [APFloat] Add exp function for APFloat::IEEESsingle using expf implementation from LLVM libc. (#143959)

Discourse RFC:
https://discourse.llvm.org/t/rfc-make-clang-builtin-math-functions-constexpr-with-llvm-libc-to-support-c-23-constexpr-math-functions/86450

- The implementation in LLVM libc is header-only.
- expf implementation in LLVM libc is correctly rounded for all rounding
modes.
- LLVM libc implementation will round to the floating point
environment's rounding mode.
- No cmake build dependency between LLVM and LLVM libc, only requires
LLVM libc source presents in llvm-project/libc folder.

Added: 
    libc/src/__support/math/expf_static_rounding.h

Modified: 
    libc/shared/math/expf.h
    llvm/CMakeLists.txt
    llvm/include/llvm/ADT/APFloat.h
    llvm/lib/Support/APFloat.cpp
    llvm/lib/Support/CMakeLists.txt
    llvm/lib/Target/AMDGPU/AMDGPULibCalls.cpp
    llvm/unittests/ADT/APFloatTest.cpp

Removed: 
    


################################################################################
diff  --git a/libc/shared/math/expf.h b/libc/shared/math/expf.h
index a4e8b0751bb42..fbcf49c15ae32 100644
--- a/libc/shared/math/expf.h
+++ b/libc/shared/math/expf.h
@@ -11,6 +11,7 @@
 
 #include "shared/libc_common.h"
 #include "src/__support/math/expf.h"
+#include "src/__support/math/expf_static_rounding.h"
 
 namespace LIBC_NAMESPACE_DECL {
 namespace shared {

diff  --git a/libc/src/__support/math/expf_static_rounding.h b/libc/src/__support/math/expf_static_rounding.h
new file mode 100644
index 0000000000000..fc0b7967adf47
--- /dev/null
+++ b/libc/src/__support/math/expf_static_rounding.h
@@ -0,0 +1,43 @@
+//===-- 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC___SUPPORT_MATH_EXPF_STATIC_ROUNDING_H
+#define LLVM_LIBC_SRC___SUPPORT_MATH_EXPF_STATIC_ROUNDING_H
+
+#include "expf.h"
+#include "src/__support/FPUtil/FEnvImpl.h"
+#include "src/__support/common.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+namespace math {
+
+// Remark: "#pragma STDC FENV_ACCESS" might generate unsupported warnings on
+// certain platforms.
+#pragma STDC FENV_ACCESS ON
+
+// Directional rounding version of expf.
+LIBC_INLINE static float expf(float x, int rounding_mode) {
+  int current_rounding_mode = fputil::get_round();
+  if (rounding_mode == current_rounding_mode)
+    return expf(x);
+
+  fputil::set_round(rounding_mode);
+  float result = expf(x);
+  fputil::set_round(current_rounding_mode);
+  return result;
+}
+
+#pragma STDC FENV_ACCESS DEFAULT
+
+} // namespace math
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC___SUPPORT_MATH_EXPF_STATIC_ROUNDING_H

diff  --git a/llvm/CMakeLists.txt b/llvm/CMakeLists.txt
index 6d94cbbcd2559..1b402eeb651dd 100644
--- a/llvm/CMakeLists.txt
+++ b/llvm/CMakeLists.txt
@@ -657,6 +657,11 @@ endif()
 
 set(LLVM_ENABLE_Z3_SOLVER_DEFAULT "${Z3_FOUND}")
 
+include(FindLibcCommonUtils)
+if(NOT TARGET llvm-libc-common-utilities)
+  message(FATAL_ERROR "LLVM libc is not found at ${libc_path}.")
+endif()
+
 
 if( LLVM_TARGETS_TO_BUILD STREQUAL "all" )
   set( LLVM_TARGETS_TO_BUILD ${LLVM_ALL_TARGETS} )

diff  --git a/llvm/include/llvm/ADT/APFloat.h b/llvm/include/llvm/ADT/APFloat.h
index b2d5ea33f888c..e3c461f6a2b55 100644
--- a/llvm/include/llvm/ADT/APFloat.h
+++ b/llvm/include/llvm/ADT/APFloat.h
@@ -1493,6 +1493,8 @@ class APFloat : public APFloatBase {
   friend APFloat frexp(const APFloat &X, int &Exp, roundingMode RM);
   friend IEEEFloat;
   friend DoubleAPFloat;
+
+  friend APFloat exp(const APFloat &X, roundingMode RM);
 };
 
 static_assert(sizeof(APFloat) == sizeof(detail::IEEEFloat),
@@ -1645,6 +1647,10 @@ inline APFloat maximumnum(const APFloat &A, const APFloat &B) {
   return A < B ? B : A;
 }
 
+/// Implement IEEE 754-2019 exp functions.
+LLVM_READONLY
+APFloat exp(const APFloat &X, RoundingMode RM = APFloat::rmNearestTiesToEven);
+
 inline raw_ostream &operator<<(raw_ostream &OS, const APFloat &V) {
   V.print(OS);
   return OS;

diff  --git a/llvm/lib/Support/APFloat.cpp b/llvm/lib/Support/APFloat.cpp
index e2645fa46bbcd..e7964e7824a81 100644
--- a/llvm/lib/Support/APFloat.cpp
+++ b/llvm/lib/Support/APFloat.cpp
@@ -28,6 +28,9 @@
 #include <cstring>
 #include <limits.h>
 
+// Shared headers from LLVM libc
+#include "shared/math.h"
+
 #define APFLOAT_DISPATCH_ON_SEMANTICS(METHOD_CALL)                             \
   do {                                                                         \
     if (usesLayout<IEEEFloat>(getSemantics()))                                 \
@@ -6155,6 +6158,29 @@ float APFloat::convertToFloat() const {
   return Temp.getIEEE().convertToFloat();
 }
 
+static constexpr int getFEnvRoundingMode(llvm::RoundingMode rm) {
+  switch (rm) {
+  case APFloat::rmTowardPositive:
+    return FE_UPWARD;
+  case APFloat::rmTowardNegative:
+    return FE_DOWNWARD;
+  case APFloat::rmTowardZero:
+    return FE_TOWARDZERO;
+  default:
+    // TODO: fix rmNearestTiesToAway for platform without FE_TONEARESTFROMZERO.
+    return FE_TONEAREST;
+  };
+}
+
+APFloat exp(const APFloat &X, RoundingMode rounding_mode) {
+  if (&X.getSemantics() == &APFloat::IEEEsingle()) {
+    float result = LIBC_NAMESPACE::shared::expf(
+        X.convertToFloat(), getFEnvRoundingMode(rounding_mode));
+    return APFloat(result);
+  }
+  llvm_unreachable("Unsupported floating-point semantics");
+}
+
 APFloat::Storage::~Storage() {
   if (usesLayout<IEEEFloat>(*semantics)) {
     IEEE.~IEEEFloat();

diff  --git a/llvm/lib/Support/CMakeLists.txt b/llvm/lib/Support/CMakeLists.txt
index 1c397e8c0b766..41eb19a97cb96 100644
--- a/llvm/lib/Support/CMakeLists.txt
+++ b/llvm/lib/Support/CMakeLists.txt
@@ -395,3 +395,9 @@ target_include_directories(LLVMSupport
   PRIVATE
   ${LLVM_THIRD_PARTY_DIR}/siphash/include
 )
+
+# Integrating LLVM libc's math functions
+target_include_directories(LLVMSupport PRIVATE "${LLVM_INCLUDE_DIR}/../../libc")
+if(NOT MSVC)
+  target_compile_options(LLVMSupport PRIVATE "-Wno-c99-extensions") # _Complex warnings.
+endif()

diff  --git a/llvm/lib/Target/AMDGPU/AMDGPULibCalls.cpp b/llvm/lib/Target/AMDGPU/AMDGPULibCalls.cpp
index 821d7f38fcb41..f83b41773d1a7 100644
--- a/llvm/lib/Target/AMDGPU/AMDGPULibCalls.cpp
+++ b/llvm/lib/Target/AMDGPU/AMDGPULibCalls.cpp
@@ -1531,7 +1531,7 @@ bool AMDGPULibCalls::evaluateScalarMathFunc(const FuncInfo &FInfo, double &Res0,
     return true;
 
   case AMDGPULibFunc::EI_EXP:
-    Res0 = exp(opr0);
+    Res0 = std::exp(opr0);
     return true;
 
   case AMDGPULibFunc::EI_EXP2:

diff  --git a/llvm/unittests/ADT/APFloatTest.cpp b/llvm/unittests/ADT/APFloatTest.cpp
index 99cc38b6b422b..427cb830c0240 100644
--- a/llvm/unittests/ADT/APFloatTest.cpp
+++ b/llvm/unittests/ADT/APFloatTest.cpp
@@ -10182,4 +10182,78 @@ TEST(APFloatTest, FrexpQuietSNaN) {
   EXPECT_FALSE(Result.isSignaling());
 }
 
+TEST(APFloatTest, expf) {
+  std::array<llvm::RoundingMode, 4> allRoundingModes = {
+      APFloat::rmNearestTiesToEven, APFloat::rmTowardPositive,
+      APFloat::rmTowardNegative, APFloat::rmTowardZero};
+  for (auto rm : allRoundingModes) {
+    // exp(+-0) = 1 for all rounding modes.
+    EXPECT_EQ(1.0f, llvm::exp(APFloat(0.0f), rm).convertToFloat());
+    EXPECT_EQ(1.0f, llvm::exp(APFloat(-0.0f), rm).convertToFloat());
+    // exp(+Inf) = +Inf for all rounding modes.
+    EXPECT_EQ(std::numeric_limits<float>::infinity(),
+              llvm::exp(APFloat::getInf(APFloat::IEEEsingle(), false), rm)
+                  .convertToFloat());
+    // exp(-Inf) = 0 for all rounding modes.
+    EXPECT_EQ(0.0f, llvm::exp(APFloat::getInf(APFloat::IEEEsingle(), true), rm)
+                        .convertToFloat());
+    // exp(NaN) = NaN for all rounding modes.
+    EXPECT_TRUE(llvm::exp(APFloat::getNaN(APFloat::IEEEsingle()), rm).isNaN());
+  }
+  // exp(1)
+  EXPECT_EQ(
+      0x1.5bf0a8p1f,
+      llvm::exp(APFloat(1.0f), APFloat::rmNearestTiesToEven).convertToFloat());
+  EXPECT_EQ(
+      0x1.5bf0aap1f,
+      llvm::exp(APFloat(1.0f), APFloat::rmTowardPositive).convertToFloat());
+  EXPECT_EQ(
+      0x1.5bf0a8p1f,
+      llvm::exp(APFloat(1.0f), APFloat::rmTowardNegative).convertToFloat());
+  EXPECT_EQ(0x1.5bf0a8p1f,
+            llvm::exp(APFloat(1.0f), APFloat::rmTowardZero).convertToFloat());
+  // exp(float max)
+  EXPECT_EQ(std::numeric_limits<float>::infinity(),
+            llvm::exp(APFloat::getLargest(APFloat::IEEEsingle(), false),
+                      APFloat::rmNearestTiesToEven)
+                .convertToFloat());
+  EXPECT_EQ(std::numeric_limits<float>::infinity(),
+            llvm::exp(APFloat::getLargest(APFloat::IEEEsingle(), false),
+                      APFloat::rmTowardPositive)
+                .convertToFloat());
+  EXPECT_EQ(std::numeric_limits<float>::max(),
+            llvm::exp(APFloat::getLargest(APFloat::IEEEsingle(), false),
+                      APFloat::rmTowardNegative)
+                .convertToFloat());
+  EXPECT_EQ(std::numeric_limits<float>::max(),
+            llvm::exp(APFloat::getLargest(APFloat::IEEEsingle(), false),
+                      APFloat::rmTowardZero)
+                .convertToFloat());
+  // exp(min_denormal)
+  EXPECT_EQ(1.0f, llvm::exp(APFloat::getSmallest(APFloat::IEEEsingle(), false),
+                            APFloat::rmNearestTiesToEven)
+                      .convertToFloat());
+  EXPECT_EQ(0x1.000002p0f,
+            llvm::exp(APFloat::getSmallest(APFloat::IEEEsingle(), false),
+                      APFloat::rmTowardPositive)
+                .convertToFloat());
+  EXPECT_EQ(1.0f, llvm::exp(APFloat::getSmallest(APFloat::IEEEsingle(), false),
+                            APFloat::rmTowardNegative)
+                      .convertToFloat());
+  EXPECT_EQ(1.0f, llvm::exp(APFloat::getSmallest(APFloat::IEEEsingle(), false),
+                            APFloat::rmTowardZero)
+                      .convertToFloat());
+  // Default rounding mode.
+  // exp(-1)
+  EXPECT_EQ(0x1.78b564p-2f, llvm::exp(APFloat(-1.0f)).convertToFloat());
+  EXPECT_EQ(
+      0x1.78b564p-2f,
+      llvm::exp(APFloat(-1.0f), APFloat::rmTowardPositive).convertToFloat());
+  EXPECT_EQ(
+      0x1.78b562p-2f,
+      llvm::exp(APFloat(-1.0f), APFloat::rmTowardNegative).convertToFloat());
+  EXPECT_EQ(0x1.78b562p-2f,
+            llvm::exp(APFloat(-1.0f), APFloat::rmTowardZero).convertToFloat());
+}
+
 } // namespace


        


More information about the libc-commits mailing list