[flang-commits] [flang] [llvm] [flang] Fix wrong result for exponentiation when denormal is expected (PR #205262)
via flang-commits
flang-commits at lists.llvm.org
Mon Jun 22 23:26:35 PDT 2026
https://github.com/ejose02 updated https://github.com/llvm/llvm-project/pull/205262
>From 47df9f5f089e3b40abe133bcf77db0bc611824da Mon Sep 17 00:00:00 2001
From: ejose02 <ejose at amd.com>
Date: Tue, 23 Jun 2026 11:13:36 +0530
Subject: [PATCH] [flang] Fix wrong result for exponentiation when denormal is
expected
Fixes #192639
Root cause:
Negative integer exponents used exponentiation-by-squaring that divides by successively squared bases; for |base| > 1 intermediates overflow before the final divide.
Fix:
For real types with negative exponent and |base| > 1, evaluate (1/base)^|exp| instead. Apply the same rule in compile-time IntPower and runtime FPowI.
---
flang-rt/lib/runtime/numeric.cpp | 4 ++++
flang-rt/unittests/Runtime/Numeric.cpp | 1 +
flang/lib/Evaluate/int-power.h | 18 ++++++++++++++++++
flang/test/Evaluate/folding07.f90 | 1 +
4 files changed, 24 insertions(+)
diff --git a/flang-rt/lib/runtime/numeric.cpp b/flang-rt/lib/runtime/numeric.cpp
index 78f148dbc5d8c..fde72c353a3c4 100644
--- a/flang-rt/lib/runtime/numeric.cpp
+++ b/flang-rt/lib/runtime/numeric.cpp
@@ -208,6 +208,10 @@ RT_API_ATTRS BTy FPowI(BTy base, ETy exp) {
} else if (isNegativePower) {
exp = -exp;
}
+ if (isNegativePower && base != BTy{0} && std::abs(base) > BTy{1}) {
+ base = BTy{1} / base;
+ isNegativePower = false;
+ }
BTy result{1};
BTy origBase{base};
while (true) {
diff --git a/flang-rt/unittests/Runtime/Numeric.cpp b/flang-rt/unittests/Runtime/Numeric.cpp
index 4baad3fc9ad0e..aeb6d6d3644f1 100644
--- a/flang-rt/unittests/Runtime/Numeric.cpp
+++ b/flang-rt/unittests/Runtime/Numeric.cpp
@@ -273,6 +273,7 @@ TEST(Numeric, FPowI) {
EXPECT_EQ(RTNAME(FPow4i)(Real<4>{0.5}, Int<4>{-1}), Real<4>{2});
EXPECT_EQ(RTNAME(FPow4i)(Real<4>{-3}, Int<4>{3}), Real<4>{-27});
EXPECT_EQ(RTNAME(FPow4i)(Real<4>{-2}, Int<4>{-3}), Real<4>{-0.125});
+ EXPECT_NE(RTNAME(FPow4i)(Real<4>{2}, Int<4>{-133}), Real<4>{0});
EXPECT_EQ(RTNAME(FPow4k)(Real<4>{0}, Int<8>{0}), Real<4>{1});
EXPECT_EQ(RTNAME(FPow4k)(Real<4>{0.3}, Int<8>{0}), Real<4>{1});
diff --git a/flang/lib/Evaluate/int-power.h b/flang/lib/Evaluate/int-power.h
index 2ee012ceb77a3..f9c0f3e9b657e 100644
--- a/flang/lib/Evaluate/int-power.h
+++ b/flang/lib/Evaluate/int-power.h
@@ -15,6 +15,10 @@
namespace Fortran::evaluate {
+namespace value {
+template <typename W, int P> class Real;
+}
+
template <typename REAL, typename INT>
ValueWithRealFlags<REAL> TimesIntPowerOf(const REAL &factor, const REAL &base,
const INT &power,
@@ -51,6 +55,20 @@ ValueWithRealFlags<REAL> TimesIntPowerOf(const REAL &factor, const REAL &base,
return result;
}
+template <typename W, int P, typename INT>
+ValueWithRealFlags<value::Real<W, P>> IntPower(const value::Real<W, P> &base,
+ const INT &power,
+ Rounding rounding = TargetCharacteristics::defaultRounding) {
+ using REAL = value::Real<W, P>;
+ REAL one{REAL::FromInteger(INT{1}).value};
+ if (power.IsNegative() && !base.IsZero() &&
+ base.ABS().Compare(one) == Relation::Greater) {
+ REAL recip{one.Divide(base, rounding).value};
+ return TimesIntPowerOf(one, recip, power.ABS().value, rounding);
+ }
+ return TimesIntPowerOf(one, base, power, rounding);
+}
+
template <typename REAL, typename INT>
ValueWithRealFlags<REAL> IntPower(const REAL &base, const INT &power,
Rounding rounding = TargetCharacteristics::defaultRounding) {
diff --git a/flang/test/Evaluate/folding07.f90 b/flang/test/Evaluate/folding07.f90
index d51df7acf7b8a..0952ae7b34847 100644
--- a/flang/test/Evaluate/folding07.f90
+++ b/flang/test/Evaluate/folding07.f90
@@ -307,5 +307,6 @@ module m
logical, parameter :: test_set_exponent_1 = set_exponent(1., 1) == 1.
logical, parameter :: test_set_exponent_2 = set_exponent(1., 2) == 2.
logical, parameter :: test_set_exponent_min = set_exponent(1., -149) == 1.40129846432481707092372958328991613128026194187651577175706828388979108268586060148663818836212158203125e-45_4
+ logical, parameter :: test_pow_denorm_neg = 2.0**(-133) /= 0.0
end module
More information about the flang-commits
mailing list