[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