//===-- lib/Evaluate/complex.cpp ------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "flang/Evaluate/complex.h" #include "llvm/Support/raw_ostream.h" namespace Fortran::evaluate::value { template ValueWithRealFlags> Complex::Add( const Complex &that, Rounding rounding) const { RealFlags flags; Part reSum{re_.Add(that.re_, rounding).AccumulateFlags(flags)}; Part imSum{im_.Add(that.im_, rounding).AccumulateFlags(flags)}; return {Complex{reSum, imSum}, flags}; } template ValueWithRealFlags> Complex::Subtract( const Complex &that, Rounding rounding) const { RealFlags flags; Part reDiff{re_.Subtract(that.re_, rounding).AccumulateFlags(flags)}; Part imDiff{im_.Subtract(that.im_, rounding).AccumulateFlags(flags)}; return {Complex{reDiff, imDiff}, flags}; } template ValueWithRealFlags> Complex::Multiply( const Complex &that, Rounding rounding) const { // (a + ib)*(c + id) -> ac - bd + i(ad + bc) RealFlags flags; Part ac{re_.Multiply(that.re_, rounding).AccumulateFlags(flags)}; Part bd{im_.Multiply(that.im_, rounding).AccumulateFlags(flags)}; Part ad{re_.Multiply(that.im_, rounding).AccumulateFlags(flags)}; Part bc{im_.Multiply(that.re_, rounding).AccumulateFlags(flags)}; Part acbd{ac.Subtract(bd, rounding).AccumulateFlags(flags)}; Part adbc{ad.Add(bc, rounding).AccumulateFlags(flags)}; return {Complex{acbd, adbc}, flags}; } template ValueWithRealFlags> Complex::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) -- note (cc+dd) is real // -> ((ac+bd)/(cc+dd)) + i((bc-ad)/(cc+dd)) 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 std::string Complex::DumpHexadecimal() const { std::string result{'('}; result += re_.DumpHexadecimal(); result += ','; result += im_.DumpHexadecimal(); result += ')'; return result; } template llvm::raw_ostream &Complex::AsFortran(llvm::raw_ostream &o, int kind) const { re_.AsFortran(o << '(', kind); im_.AsFortran(o << ',', kind); return o << ')'; } template class Complex, 11>>; template class Complex, 8>>; template class Complex, 24>>; template class Complex, 53>>; template class Complex, 64>>; template class Complex, 113>>; } // namespace Fortran::evaluate::value