[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
Fri Oct 9 04:53:59 PDT 2026
https://github.com/ejose02 updated https://github.com/llvm/llvm-project/pull/205262
>From c530b455638575dc29559828973f74c0f85bbdd4 Mon Sep 17 00:00:00 2001
From: ejose02 <ejose at amd.com>
Date: Tue, 23 Jun 2026 11:13:36 +0530
Subject: [PATCH 1/3] [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 78f148dbc5d8ce..fde72c353a3c47 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 4baad3fc9ad0eb..aeb6d6d3644f1f 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 2ee012ceb77a38..f9c0f3e9b657e4 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 d51df7acf7b8a6..0952ae7b348479 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
>From 8deba6c60581a8932ebe5782a9bf3d7702078555 Mon Sep 17 00:00:00 2001
From: ejose <ejose at amd.com>
Date: Thu, 8 Oct 2026 11:18:30 +0000
Subject: [PATCH 2/3] Use ABSTy in FPowI and propagate IntPower divide flags
---
flang-rt/lib/runtime/numeric.cpp | 2 +-
flang/lib/Evaluate/int-power.h | 7 +++++--
2 files changed, 6 insertions(+), 3 deletions(-)
diff --git a/flang-rt/lib/runtime/numeric.cpp b/flang-rt/lib/runtime/numeric.cpp
index fde72c353a3c47..272fee336d18d1 100644
--- a/flang-rt/lib/runtime/numeric.cpp
+++ b/flang-rt/lib/runtime/numeric.cpp
@@ -208,7 +208,7 @@ RT_API_ATTRS BTy FPowI(BTy base, ETy exp) {
} else if (isNegativePower) {
exp = -exp;
}
- if (isNegativePower && base != BTy{0} && std::abs(base) > BTy{1}) {
+ if (isNegativePower && base != BTy{0} && ABSTy<BTy>::compute(base) > BTy{1}) {
base = BTy{1} / base;
isNegativePower = false;
}
diff --git a/flang/lib/Evaluate/int-power.h b/flang/lib/Evaluate/int-power.h
index f9c0f3e9b657e4..c3d8e5ae1aa31c 100644
--- a/flang/lib/Evaluate/int-power.h
+++ b/flang/lib/Evaluate/int-power.h
@@ -63,8 +63,11 @@ ValueWithRealFlags<value::Real<W, P>> IntPower(const value::Real<W, P> &base,
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);
+ ValueWithRealFlags<REAL> result;
+ REAL recip{one.Divide(base, rounding).AccumulateFlags(result.flags)};
+ result.value = TimesIntPowerOf(one, recip, power.ABS().value, rounding)
+ .AccumulateFlags(result.flags);
+ return result;
}
return TimesIntPowerOf(one, base, power, rounding);
}
>From f0c1f50558d3b8a0116e97dd5ae0fec4c06db104 Mon Sep 17 00:00:00 2001
From: ejose <ejose at amd.com>
Date: Fri, 9 Oct 2026 11:32:58 +0000
Subject: [PATCH 3/3] [flang] Overflow-gated IntPower retry and stronger pow
tests
---
flang-rt/unittests/Runtime/Numeric.cpp | 9 +++++++-
flang/lib/Evaluate/int-power.h | 30 +++++++++++++-------------
flang/test/Evaluate/folding07.f90 | 5 ++++-
3 files changed, 27 insertions(+), 17 deletions(-)
diff --git a/flang-rt/unittests/Runtime/Numeric.cpp b/flang-rt/unittests/Runtime/Numeric.cpp
index aeb6d6d3644f1f..ba8b9ae5f23006 100644
--- a/flang-rt/unittests/Runtime/Numeric.cpp
+++ b/flang-rt/unittests/Runtime/Numeric.cpp
@@ -273,7 +273,10 @@ 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(FPow4i)(Real<4>{2}, Int<4>{-133}), std::ldexp(Real<4>{1}, -133));
+ EXPECT_EQ(RTNAME(FPow4i)(Real<4>{2}, std::numeric_limits<Int<4>>::min()),
+ 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});
@@ -281,6 +284,8 @@ TEST(Numeric, FPowI) {
EXPECT_EQ(RTNAME(FPow4k)(Real<4>{0.5}, Int<8>{-1}), Real<4>{2});
EXPECT_EQ(RTNAME(FPow4k)(Real<4>{-3}, Int<8>{3}), Real<4>{-27});
EXPECT_EQ(RTNAME(FPow4k)(Real<4>{-2}, Int<8>{-3}), Real<4>{-0.125});
+ EXPECT_EQ(
+ RTNAME(FPow4k)(Real<4>{2}, Int<8>{-133}), std::ldexp(Real<4>{1}, -133));
EXPECT_EQ(RTNAME(FPow8i)(Real<8>{0}, Int<4>{0}), Real<8>{1});
EXPECT_EQ(RTNAME(FPow8i)(Real<8>{0.3}, Int<4>{0}), Real<8>{1});
@@ -288,6 +293,8 @@ TEST(Numeric, FPowI) {
EXPECT_EQ(RTNAME(FPow8i)(Real<8>{0.5}, Int<4>{-1}), Real<8>{2});
EXPECT_EQ(RTNAME(FPow8i)(Real<8>{-3}, Int<4>{3}), Real<8>{-27});
EXPECT_EQ(RTNAME(FPow8i)(Real<8>{-2}, Int<4>{-3}), Real<8>{-0.125});
+ EXPECT_EQ(
+ RTNAME(FPow8i)(Real<8>{2}, Int<4>{-1070}), std::ldexp(Real<8>{1}, -1070));
EXPECT_EQ(RTNAME(FPow8k)(Real<8>{0}, Int<8>{0}), Real<8>{1});
EXPECT_EQ(RTNAME(FPow8k)(Real<8>{0.3}, Int<8>{0}), Real<8>{1});
diff --git a/flang/lib/Evaluate/int-power.h b/flang/lib/Evaluate/int-power.h
index c3d8e5ae1aa31c..3f9aa1b1612a1f 100644
--- a/flang/lib/Evaluate/int-power.h
+++ b/flang/lib/Evaluate/int-power.h
@@ -11,17 +11,14 @@
// Computes an integer power of a real or complex value.
+#include "flang/Evaluate/real.h"
#include "flang/Evaluate/target.h"
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,
+ const INT &power, bool forceMultiply = false,
Rounding rounding = TargetCharacteristics::defaultRounding) {
ValueWithRealFlags<REAL> result{factor};
if (base.IsNotANumber()) {
@@ -32,7 +29,7 @@ ValueWithRealFlags<REAL> TimesIntPowerOf(const REAL &factor, const REAL &base,
result.flags.set(RealFlag::InvalidArgument);
}
} else {
- bool negativePower{power.IsNegative()};
+ bool negativePower{!forceMultiply && power.IsNegative()};
INT absPower{power.ABS().value};
REAL squares{base};
int nbits{INT::bits - absPower.LEADZ()};
@@ -61,22 +58,25 @@ ValueWithRealFlags<value::Real<W, P>> IntPower(const value::Real<W, P> &base,
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) {
- ValueWithRealFlags<REAL> result;
- REAL recip{one.Divide(base, rounding).AccumulateFlags(result.flags)};
- result.value = TimesIntPowerOf(one, recip, power.ABS().value, rounding)
- .AccumulateFlags(result.flags);
- return result;
+ ValueWithRealFlags<REAL> result{
+ TimesIntPowerOf(one, base, power, /*forceMultiply=*/false, rounding)};
+ if (result.flags.test(RealFlag::Overflow) && power.IsNegative() &&
+ !base.IsZero() && base.ABS().Compare(one) == Relation::Greater) {
+ ValueWithRealFlags<REAL> retry;
+ REAL recip{one.Divide(base, rounding).AccumulateFlags(retry.flags)};
+ retry.value =
+ TimesIntPowerOf(one, recip, power, /*forceMultiply=*/true, rounding)
+ .AccumulateFlags(retry.flags);
+ return retry;
}
- return TimesIntPowerOf(one, base, power, rounding);
+ return result;
}
template <typename REAL, typename INT>
ValueWithRealFlags<REAL> IntPower(const REAL &base, const INT &power,
Rounding rounding = TargetCharacteristics::defaultRounding) {
REAL one{REAL::FromInteger(INT{1}).value};
- return TimesIntPowerOf(one, base, power, rounding);
+ return TimesIntPowerOf(one, base, power, /*forceMultiply=*/false, rounding);
}
} // namespace Fortran::evaluate
#endif // FORTRAN_EVALUATE_INT_POWER_H_
diff --git a/flang/test/Evaluate/folding07.f90 b/flang/test/Evaluate/folding07.f90
index 0952ae7b348479..8fc0a20f246eb7 100644
--- a/flang/test/Evaluate/folding07.f90
+++ b/flang/test/Evaluate/folding07.f90
@@ -307,6 +307,9 @@ 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
+ logical, parameter :: test_pow_subnormal_4 = 2.0_4**(-133) == scale(1.0_4, -133)
+ logical, parameter :: test_pow_neg_3 = 3.0**(-2) == 1.0/3.0**2
+ logical, parameter :: test_pow_neg_7 = 7.0**(-5) == 1.0/7.0**5
+ logical, parameter :: test_pow_min_int1 = 1.5_8**(-128_1) < 1.0_8
end module
More information about the flang-commits
mailing list