[flang-commits] [flang] 715283a - [flang] Cope with overflow in real MOD/MODULO

Peter Klausler via flang-commits flang-commits at lists.llvm.org
Wed Jul 6 17:35:06 PDT 2022


Author: Peter Klausler
Date: 2022-07-06T17:31:11-07:00
New Revision: 715283aa1792ed1dc0cf513128f272fd92dd06da

URL: https://github.com/llvm/llvm-project/commit/715283aa1792ed1dc0cf513128f272fd92dd06da
DIFF: https://github.com/llvm/llvm-project/commit/715283aa1792ed1dc0cf513128f272fd92dd06da.diff

LOG: [flang] Cope with overflow in real MOD/MODULO

In folding and in the runtime library for real MOD/MODULO(A,P),
detect overflow from the division A/P and return a properly signed
zero result.  (When A/P overflows and both A and P are finite numbers
with nonzero P, the quotient would be a large integer when rounded to
the precision of the floating-point representation.)

Differential Revision: https://reviews.llvm.org/D129020

Added: 
    

Modified: 
    flang/lib/Evaluate/real.cpp
    flang/runtime/numeric.cpp
    flang/test/Evaluate/fold-mod.f90

Removed: 
    


################################################################################
diff  --git a/flang/lib/Evaluate/real.cpp b/flang/lib/Evaluate/real.cpp
index b7230f891fa80..c22d3c886609e 100644
--- a/flang/lib/Evaluate/real.cpp
+++ b/flang/lib/Evaluate/real.cpp
@@ -406,11 +406,21 @@ template <typename W, int P>
 ValueWithRealFlags<Real<W, P>> Real<W, P>::MOD(
     const Real &y, Rounding rounding) const {
   ValueWithRealFlags<Real> result;
-  Real quotient{Divide(y, rounding).AccumulateFlags(result.flags)};
-  Real toInt{quotient.ToWholeNumber(common::RoundingMode::ToZero)
-                 .AccumulateFlags(result.flags)};
-  Real product{toInt.Multiply(y, rounding).AccumulateFlags(result.flags)};
-  result.value = Subtract(product, rounding).AccumulateFlags(result.flags);
+  auto quotient{Divide(y, rounding)};
+  if (quotient.value.IsInfinite() && IsFinite() && y.IsFinite() &&
+      !y.IsZero()) {
+    // x/y overflowed -- so it must be an integer in this representation and
+    // the result must be a zero.
+    if (IsNegative()) {
+      result.value = Real{}.Negate(); // -0.
+    }
+  } else {
+    Real toInt{quotient.AccumulateFlags(result.flags)
+                   .ToWholeNumber(common::RoundingMode::ToZero)
+                   .AccumulateFlags(result.flags)};
+    Real product{toInt.Multiply(y, rounding).AccumulateFlags(result.flags)};
+    result.value = Subtract(product, rounding).AccumulateFlags(result.flags);
+  }
   return result;
 }
 
@@ -419,11 +429,21 @@ template <typename W, int P>
 ValueWithRealFlags<Real<W, P>> Real<W, P>::MODULO(
     const Real &y, Rounding rounding) const {
   ValueWithRealFlags<Real> result;
-  Real quotient{Divide(y, rounding).AccumulateFlags(result.flags)};
-  Real toInt{quotient.ToWholeNumber(common::RoundingMode::Down)
-                 .AccumulateFlags(result.flags)};
-  Real product{toInt.Multiply(y, rounding).AccumulateFlags(result.flags)};
-  result.value = Subtract(product, rounding).AccumulateFlags(result.flags);
+  auto quotient{Divide(y, rounding)};
+  if (quotient.value.IsInfinite() && IsFinite() && y.IsFinite() &&
+      !y.IsZero()) {
+    // x/y overflowed -- so it must be an integer in this representation and
+    // the result must be a zero.
+    if (y.IsNegative()) {
+      result.value = Real{}.Negate(); // -0.
+    }
+  } else {
+    Real toInt{quotient.AccumulateFlags(result.flags)
+                   .ToWholeNumber(common::RoundingMode::Down)
+                   .AccumulateFlags(result.flags)};
+    Real product{toInt.Multiply(y, rounding).AccumulateFlags(result.flags)};
+    result.value = Subtract(product, rounding).AccumulateFlags(result.flags);
+  }
   return result;
 }
 

diff  --git a/flang/runtime/numeric.cpp b/flang/runtime/numeric.cpp
index 391837bc4752b..8d0297317b4df 100644
--- a/flang/runtime/numeric.cpp
+++ b/flang/runtime/numeric.cpp
@@ -77,16 +77,19 @@ inline T IntMod(T x, T p, const char *sourceFile, int sourceLine) {
   return mod;
 }
 template <bool IS_MODULO, typename T>
-inline T RealMod(T x, T p, const char *sourceFile, int sourceLine) {
+inline T RealMod(T a, T p, const char *sourceFile, int sourceLine) {
   if (p == 0) {
     Terminator{sourceFile, sourceLine}.Crash(
         IS_MODULO ? "MODULO with P==0" : "MOD with P==0");
   }
-  if constexpr (IS_MODULO) {
-    return x - std::floor(x / p) * p;
-  } else {
-    return x - std::trunc(x / p) * p;
+  T quotient{a / p};
+  if (std::isinf(quotient) && std::isfinite(a) && std::isfinite(p)) {
+    // a/p overflowed -- so it must be an integer, and the result
+    // must be a zero of the same sign as one of the operands.
+    return std::copysign(T{}, IS_MODULO ? p : a);
   }
+  T toInt{IS_MODULO ? std::floor(quotient) : std::trunc(quotient)};
+  return a - toInt * p;
 }
 
 // RRSPACING (16.9.164)

diff  --git a/flang/test/Evaluate/fold-mod.f90 b/flang/test/Evaluate/fold-mod.f90
index 6b5188a39b215..96c35cb6e4708 100644
--- a/flang/test/Evaluate/fold-mod.f90
+++ b/flang/test/Evaluate/fold-mod.f90
@@ -12,6 +12,15 @@ module m1
   logical, parameter :: test_mod_r4 = mod(8., -5.) == 3.
   logical, parameter :: test_mod_r5 = mod(-8., -5.) == -3.
 
+  logical, parameter :: test_mod_r10a = mod(huge(0.), tiny(0.)) == 0.
+  logical, parameter :: test_mod_r10b = sign(1., mod(huge(0.), tiny(0.))) == 1.
+  logical, parameter :: test_mod_r11a = mod(-huge(0.), tiny(0.)) == 0.
+  logical, parameter :: test_mod_r11b = sign(1., mod(-huge(0.), tiny(0.))) == -1.
+  logical, parameter :: test_mod_r12a = mod(huge(0.), -tiny(0.)) == 0.
+  logical, parameter :: test_mod_r12b = sign(1., mod(huge(0.), -tiny(0.))) == 1.
+  logical, parameter :: test_mod_r13a = mod(huge(0.), tiny(0.)) == 0.
+  logical, parameter :: test_mod_r13b = sign(1., mod(-huge(0.), -tiny(0.))) == -1.
+
   logical, parameter :: test_modulo_i1 = modulo(8, 5) == 3
   logical, parameter :: test_modulo_i2 = modulo(-8, 5) == 2
   logical, parameter :: test_modulo_i3 = modulo(8, -5) == -2
@@ -21,4 +30,13 @@ module m1
   logical, parameter :: test_modulo_r2 = modulo(-8., 5.) == 2.
   logical, parameter :: test_modulo_r3 = modulo(8., -5.) == -2.
   logical, parameter :: test_modulo_r4 = modulo(-8., -5.) == -3.
+
+  logical, parameter :: test_modulo_r10a = modulo(huge(0.), tiny(0.)) == 0.
+  logical, parameter :: test_modulo_r10b = sign(1., modulo(huge(0.), tiny(0.))) == 1.
+  logical, parameter :: test_modulo_r11a = modulo(-huge(0.), tiny(0.)) == 0.
+  logical, parameter :: test_modulo_r11b = sign(1., modulo(-huge(0.), tiny(0.))) == 1.
+  logical, parameter :: test_modulo_r12a = modulo(huge(0.), -tiny(0.)) == 0.
+  logical, parameter :: test_modulo_r12b = sign(1., modulo(huge(0.), -tiny(0.))) == -1.
+  logical, parameter :: test_modulo_r13a = modulo(huge(0.), tiny(0.)) == 0.
+  logical, parameter :: test_modulo_r13b = sign(1., modulo(-huge(0.), -tiny(0.))) == -1.
 end module


        


More information about the flang-commits mailing list