[llvm] [llvm] Add floating point exception status for APFloat's exp function. (PR #203066)

via llvm-commits llvm-commits at lists.llvm.org
Wed Jun 10 11:57:59 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-llvm-support

Author: lntue

<details>
<summary>Changes</summary>

- Add extra Status return parameter for possible float point exceptions.
- Use `LIBC_NAMESPACE::shared::check::exp_exceptions(x, rm)` to test for floating point exceptions.
- Change `exp` return type to `std::optional<APFloat>` to avoid `llvm_unreachable` and be able to tell other callers like clang's `ExprConstant` about currently unsupported types and rounding modes.

---
Full diff: https://github.com/llvm/llvm-project/pull/203066.diff


3 Files Affected:

- (modified) llvm/include/llvm/ADT/APFloat.h (+4-1) 
- (modified) llvm/lib/Support/APFloat.cpp (+36-4) 
- (modified) llvm/unittests/ADT/APFloatTest.cpp (+73-22) 


``````````diff
diff --git a/llvm/include/llvm/ADT/APFloat.h b/llvm/include/llvm/ADT/APFloat.h
index 07e509baa5f1c..93f26aa43f634 100644
--- a/llvm/include/llvm/ADT/APFloat.h
+++ b/llvm/include/llvm/ADT/APFloat.h
@@ -22,6 +22,7 @@
 #include "llvm/Support/ErrorHandling.h"
 #include "llvm/Support/float128.h"
 #include <memory>
+#include <optional>
 
 #define APFLOAT_DISPATCH_ON_SEMANTICS(METHOD_CALL)                             \
   do {                                                                         \
@@ -1762,7 +1763,9 @@ inline APFloat maximumnum(const APFloat &A, const APFloat &B) {
 
 /// Implement IEEE 754-2019 exp functions
 LLVM_READONLY
-APFloat exp(const APFloat &X, RoundingMode RM = APFloat::rmNearestTiesToEven);
+std::optional<APFloat> exp(const APFloat &X,
+                           RoundingMode RM = APFloat::rmNearestTiesToEven,
+                           APFloat::opStatus *Status = nullptr);
 
 inline raw_ostream &operator<<(raw_ostream &OS, const APFloat &V) {
   V.print(OS);
diff --git a/llvm/lib/Support/APFloat.cpp b/llvm/lib/Support/APFloat.cpp
index ee3e0fee5b4bf..802c3464292af 100644
--- a/llvm/lib/Support/APFloat.cpp
+++ b/llvm/lib/Support/APFloat.cpp
@@ -41,6 +41,7 @@
 #define LIBC_MATH (LIBC_MATH_NO_ERRNO | LIBC_MATH_NO_EXCEPT)
 
 #include "shared/math.h"
+#include "shared/math_check_exceptions.h"
 
 #define APFLOAT_DISPATCH_ON_SEMANTICS(METHOD_CALL)                             \
   do {                                                                         \
@@ -6093,22 +6094,53 @@ APFloat::Storage &APFloat::Storage::operator=(APFloat::Storage &&RHS) {
   return *this;
 }
 
+namespace {
+
+APFloat::opStatus getOpStatusFromLibc(int libc_exceptions) {
+  APFloat::opStatus status = APFloat::opOK;
+  if (libc_exceptions & FE_INVALID)
+    status = static_cast<APFloat::opStatus>(status | APFloat::opInvalidOp);
+  if (libc_exceptions & FE_DIVBYZERO)
+    status = static_cast<APFloat::opStatus>(status | APFloat::opDivByZero);
+  if (libc_exceptions & FE_OVERFLOW)
+    status = static_cast<APFloat::opStatus>(status | APFloat::opOverflow);
+  if (libc_exceptions & FE_UNDERFLOW)
+    status = static_cast<APFloat::opStatus>(status | APFloat::opUnderflow);
+  if (libc_exceptions & FE_INEXACT)
+    status = static_cast<APFloat::opStatus>(status | APFloat::opInexact);
+  return status;
+}
+
+} // namespace
+
 // TODO: Support other rounding modes when LLVM libc math implement static
 // roundings.
-APFloat exp(const APFloat &X, RoundingMode rounding_mode) {
+std::optional<APFloat> exp(const APFloat &X, RoundingMode rounding_mode,
+                           APFloat::opStatus *Status) {
+
   if (rounding_mode == APFloatBase::rmNearestTiesToEven) {
     if (APFloat::SemanticsToEnum(X.getSemantics()) ==
         APFloatBase::S_IEEEsingle) {
-      float result = LIBC_NAMESPACE::shared::expf(X.convertToFloat());
+      float x_val = X.convertToFloat();
+      int exc =
+          LIBC_NAMESPACE::shared::check::exp_exceptions(x_val, FE_TONEAREST);
+      if (Status)
+        *Status = getOpStatusFromLibc(exc);
+      float result = LIBC_NAMESPACE::shared::expf(x_val);
       return APFloat(result);
     }
     if (APFloat::SemanticsToEnum(X.getSemantics()) ==
         APFloatBase::S_IEEEdouble) {
-      double result = LIBC_NAMESPACE::shared::exp(X.convertToDouble());
+      double x_val = X.convertToDouble();
+      int exc =
+          LIBC_NAMESPACE::shared::check::exp_exceptions(x_val, FE_TONEAREST);
+      if (Status)
+        *Status = getOpStatusFromLibc(exc);
+      double result = LIBC_NAMESPACE::shared::exp(x_val);
       return APFloat(result);
     }
   }
-  llvm_unreachable("Unexpected semantics");
+  return std::nullopt;
 }
 
 } // namespace llvm
diff --git a/llvm/unittests/ADT/APFloatTest.cpp b/llvm/unittests/ADT/APFloatTest.cpp
index b25932cd1d94b..6841d4bcb6093 100644
--- a/llvm/unittests/ADT/APFloatTest.cpp
+++ b/llvm/unittests/ADT/APFloatTest.cpp
@@ -10230,60 +10230,111 @@ TEST(APFloatTest, DecimalStringPreservesInexactStatus) {
 
 TEST(APFloatTest, expf) {
   // exp(+-0) = 1.
-  EXPECT_EQ(1.0f, llvm::exp(APFloat(0.0f)).convertToFloat());
-  EXPECT_EQ(1.0f, llvm::exp(APFloat(-0.0f)).convertToFloat());
+  EXPECT_EQ(1.0f, llvm::exp(APFloat(0.0f))->convertToFloat());
+  EXPECT_EQ(1.0f, llvm::exp(APFloat(-0.0f))->convertToFloat());
   // exp(+Inf) = +Inf.
   EXPECT_EQ(std::numeric_limits<float>::infinity(),
             llvm::exp(APFloat::getInf(APFloat::IEEEsingle(), false))
-                .convertToFloat());
+                ->convertToFloat());
   // exp(-Inf) = 0.
-  EXPECT_EQ(
-      0.0f,
-      llvm::exp(APFloat::getInf(APFloat::IEEEsingle(), true)).convertToFloat());
+  EXPECT_EQ(0.0f, llvm::exp(APFloat::getInf(APFloat::IEEEsingle(), true))
+                      ->convertToFloat());
   // exp(NaN) = NaN.
-  EXPECT_TRUE(llvm::exp(APFloat::getNaN(APFloat::IEEEsingle())).isNaN());
+  EXPECT_TRUE(llvm::exp(APFloat::getNaN(APFloat::IEEEsingle()))->isNaN());
   // exp(1)
-  EXPECT_EQ(0x1.5bf0a8p1f, llvm::exp(APFloat(1.0f)).convertToFloat());
+  EXPECT_EQ(0x1.5bf0a8p1f, llvm::exp(APFloat(1.0f))->convertToFloat());
   // exp(float max)
   EXPECT_EQ(std::numeric_limits<float>::infinity(),
             llvm::exp(APFloat::getLargest(APFloat::IEEEsingle(), false))
-                .convertToFloat());
+                ->convertToFloat());
   // exp(min_denormal)
   EXPECT_EQ(1.0f, llvm::exp(APFloat::getSmallest(APFloat::IEEEsingle(), false))
-                      .convertToFloat());
+                      ->convertToFloat());
   // exp(-1)
-  EXPECT_EQ(0x1.78b564p-2f, llvm::exp(APFloat(-1.0f)).convertToFloat());
+  EXPECT_EQ(0x1.78b564p-2f, llvm::exp(APFloat(-1.0f))->convertToFloat());
   // exp(-90)
-  EXPECT_EQ(0x1.1d85p-130f, llvm::exp(APFloat(-90.0f)).convertToFloat());
+  EXPECT_EQ(0x1.1d85p-130f, llvm::exp(APFloat(-90.0f))->convertToFloat());
 }
 
 TEST(APFloatTest, exp) {
   // exp(+-0) = 1.
-  EXPECT_EQ(1.0, llvm::exp(APFloat(0.0)).convertToDouble());
-  EXPECT_EQ(1.0, llvm::exp(APFloat(-0.0)).convertToDouble());
+  EXPECT_EQ(1.0, llvm::exp(APFloat(0.0))->convertToDouble());
+  EXPECT_EQ(1.0, llvm::exp(APFloat(-0.0))->convertToDouble());
   // exp(+Inf) = +Inf.
   EXPECT_EQ(std::numeric_limits<double>::infinity(),
             llvm::exp(APFloat::getInf(APFloat::IEEEdouble(), false))
-                .convertToDouble());
+                ->convertToDouble());
   // exp(-Inf) = 0.
   EXPECT_EQ(0.0, llvm::exp(APFloat::getInf(APFloat::IEEEdouble(), true))
-                     .convertToDouble());
+                     ->convertToDouble());
   // exp(NaN) = NaN.
-  EXPECT_TRUE(llvm::exp(APFloat::getNaN(APFloat::IEEEdouble())).isNaN());
+  EXPECT_TRUE(llvm::exp(APFloat::getNaN(APFloat::IEEEdouble()))->isNaN());
   // exp(1)
-  EXPECT_EQ(0x1.5bf0a8b145769p1, llvm::exp(APFloat(1.0)).convertToDouble());
+  EXPECT_EQ(0x1.5bf0a8b145769p1, llvm::exp(APFloat(1.0))->convertToDouble());
   // exp(float max)
   EXPECT_EQ(std::numeric_limits<double>::infinity(),
             llvm::exp(APFloat::getLargest(APFloat::IEEEdouble(), false))
-                .convertToDouble());
+                ->convertToDouble());
   // exp(min_denormal)
   EXPECT_EQ(1.0, llvm::exp(APFloat::getSmallest(APFloat::IEEEdouble(), false))
-                     .convertToDouble());
+                     ->convertToDouble());
   // exp(-1)
-  EXPECT_EQ(0x1.78b56362cef38p-2, llvm::exp(APFloat(-1.0)).convertToDouble());
+  EXPECT_EQ(0x1.78b56362cef38p-2, llvm::exp(APFloat(-1.0))->convertToDouble());
   // exp(-710)
   EXPECT_EQ(0x1.9c017e9459e18p-1025,
-            llvm::exp(APFloat(-710.0)).convertToDouble());
+            llvm::exp(APFloat(-710.0))->convertToDouble());
+}
+
+TEST(APFloatTest, exp_exceptions) {
+  APFloat::opStatus status;
+
+  // exp(0) should be exact (no inexact, no overflow/underflow -> opOK).
+  status = APFloat::opInvalidOp; // initialize to a dummy flag
+  auto res1 = llvm::exp(APFloat(0.0f), APFloat::rmNearestTiesToEven, &status);
+  EXPECT_TRUE(res1.has_value());
+  EXPECT_EQ(APFloat::opOK, status);
+
+  // exp(1) should be inexact (not representing an exact power of 2).
+  status = APFloat::opOK;
+  auto res2 = llvm::exp(APFloat(1.0f), APFloat::rmNearestTiesToEven, &status);
+  EXPECT_TRUE(res2.has_value());
+  EXPECT_EQ(APFloat::opInexact, status);
+
+  // exp(float max) should overflow and be inexact.
+  status = APFloat::opOK;
+  auto res3 = llvm::exp(APFloat::getLargest(APFloat::IEEEsingle(), false),
+                        APFloat::rmNearestTiesToEven, &status);
+  EXPECT_TRUE(res3.has_value());
+  EXPECT_EQ(
+      static_cast<APFloat::opStatus>(APFloat::opOverflow | APFloat::opInexact),
+      status);
+
+  // exp(-90.0f) should underflow and be inexact.
+  status = APFloat::opOK;
+  auto res4 = llvm::exp(APFloat(-90.0f), APFloat::rmNearestTiesToEven, &status);
+  EXPECT_TRUE(res4.has_value());
+  EXPECT_EQ(
+      static_cast<APFloat::opStatus>(APFloat::opUnderflow | APFloat::opInexact),
+      status);
+
+  // exp(NaN) should be quiet and not raise any exceptions.
+  status = APFloat::opInvalidOp;
+  auto res5 = llvm::exp(APFloat::getNaN(APFloat::IEEEsingle()),
+                        APFloat::rmNearestTiesToEven, &status);
+  EXPECT_TRUE(res5.has_value());
+  EXPECT_EQ(APFloat::opOK, status);
+
+  // exp(sNaN) should raise an invalid operation exception.
+  status = APFloat::opOK;
+  auto res6 = llvm::exp(APFloat::getSNaN(APFloat::IEEEsingle()),
+                        APFloat::rmNearestTiesToEven, &status);
+  EXPECT_TRUE(res6.has_value());
+  EXPECT_EQ(APFloat::opInvalidOp, status);
+
+  // exp with unsupported rounding mode or unsupported semantics should
+  // return std::nullopt.
+  EXPECT_FALSE(llvm::exp(APFloat(1.0f), APFloat::rmTowardPositive).has_value());
+  EXPECT_FALSE(llvm::exp(APFloat::getZero(APFloat::IEEEhalf())).has_value());
 }
 
 } // namespace

``````````

</details>


https://github.com/llvm/llvm-project/pull/203066


More information about the llvm-commits mailing list