[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