[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