Skip to content

Commit

Permalink
[flang] Use naive algorithm for folding complex division when it does…
Browse files Browse the repository at this point in the history
…n't over/underflow

f18 unconditionally uses a scaling algorithm for complex/complex division
that avoids needless overflows and underflows when computing the sum of
the squares of the components of the denominator -- but testing has shown
some 1 ULP differences relative to the naive calculation due to the
extra operations and roundings.  So use the scaling algorithm only when
the naive calculation actually would overflow or underflow.

Differential Revision: https://reviews.llvm.org/D132164
  • Loading branch information
klausler committed Aug 18, 2022
1 parent dcbfabb commit 2b5cd37
Showing 1 changed file with 22 additions and 3 deletions.
25 changes: 22 additions & 3 deletions flang/lib/Evaluate/complex.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,30 @@ template <typename R>
ValueWithRealFlags<Complex<R>> Complex<R>::Divide(
const Complex &that, Rounding rounding) const {
// (a + ib)/(c + id) -> [(a+ib)*(c-id)] / [(c+id)*(c-id)]
// -> [ac+bd+i(bc-ad)] / (cc+dd)
// -> [ac+bd+i(bc-ad)] / (cc+dd) -- note (cc+dd) is real
// -> ((ac+bd)/(cc+dd)) + i((bc-ad)/(cc+dd))
// but to avoid overflows, scale by d/c if c>=d, else c/d
Part scale; // <= 1.0
RealFlags flags;
Part cc{that.re_.Multiply(that.re_, rounding).AccumulateFlags(flags)};
Part dd{that.im_.Multiply(that.im_, rounding).AccumulateFlags(flags)};
Part ccPdd{cc.Add(dd, rounding).AccumulateFlags(flags)};
if (!flags.test(RealFlag::Overflow) && !flags.test(RealFlag::Underflow)) {
// den = (cc+dd) did not overflow or underflow; try the naive
// sequence without scaling to avoid extra roundings.
Part ac{re_.Multiply(that.re_, rounding).AccumulateFlags(flags)};
Part ad{re_.Multiply(that.im_, rounding).AccumulateFlags(flags)};
Part bc{im_.Multiply(that.re_, rounding).AccumulateFlags(flags)};
Part bd{im_.Multiply(that.im_, rounding).AccumulateFlags(flags)};
Part acPbd{ac.Add(bd, rounding).AccumulateFlags(flags)};
Part bcSad{bc.Subtract(ad, rounding).AccumulateFlags(flags)};
Part re{acPbd.Divide(ccPdd, rounding).AccumulateFlags(flags)};
Part im{bcSad.Divide(ccPdd, rounding).AccumulateFlags(flags)};
if (!flags.test(RealFlag::Overflow) && !flags.test(RealFlag::Underflow)) {
return {Complex{re, im}, flags};
}
}
// Scale numerator and denominator by d/c (if c>=d) or c/d (if c<d)
flags.clear();
Part scale; // will be <= 1.0 in magnitude
bool cGEd{that.re_.ABS().Compare(that.im_.ABS()) != Relation::Less};
if (cGEd) {
scale = that.im_.Divide(that.re_, rounding).AccumulateFlags(flags);
Expand Down

0 comments on commit 2b5cd37

Please sign in to comment.