[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:21:49 PDT 2026


https://github.com/ejose02 updated https://github.com/llvm/llvm-project/pull/205262

>From 73325240e4e69a4fe66a239aaa321e14e49a0543 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..e007bdc74358d 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