[flang] COMPLEX

Original-commit: flang-compiler/f18@452d602fbb
Reviewed-on: https://github.com/flang-compiler/f18/pull/101
Tree-same-pre-rewrite: false
This commit is contained in:
peter klausler 2018-06-12 15:14:18 -07:00
parent ed71134af7
commit 2391eb8de9
6 changed files with 160 additions and 28 deletions

View File

@ -75,6 +75,10 @@ enum class RealFlag {
using RealFlags = semantics::EnumSet<RealFlag, 5>;
template<typename A> struct ValueWithRealFlags {
A AccumulateFlags(RealFlags &f) {
f |= flags;
return value;
}
A value;
RealFlags flags;
};

View File

@ -0,0 +1,116 @@
// Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef FORTRAN_EVALUATE_COMPLEX_H_
#define FORTRAN_EVALUATE_COMPLEX_H_
#include "real.h"
namespace Fortran::evaluate::value {
template<typename REAL_TYPE> class Complex {
public:
using Part = REAL_TYPE;
static constexpr int bits{2 * Part::bits};
constexpr Complex() {} // (+0.0, +0.0)
constexpr Complex(const Complex &) = default;
constexpr Complex(const Part &r, const Part &i) : re_{r}, im_{i} {}
explicit constexpr Complex(const Part &r) : re_{r} {}
// TODO: (C)ABS, unit testing
constexpr const Part &REAL() const { return re_; }
constexpr const Part &AIMAG() const { return im_; }
constexpr Complex CONJG() const { return {re_, im_.Negate()}; }
constexpr Complex Negate() const { return {re_.Negate(), im_.Negate()}; }
constexpr bool Equals(const Complex &that) const {
return re_.Compare(that.re_) == Relation::Equal &&
im_.Compare(that.im_) == Relation::Equal;
}
constexpr ValueWithRealFlags<Complex> Add(const Complex &that) const {
RealFlags flags;
Part reSum{re_.Add(that.re_).AccumulateFlags(flags)};
Part imSum{im_.Add(that.im_).AccumulateFlags(flags)};
return {Complex{reSum, imSum}, flags};
}
constexpr ValueWithRealFlags<Complex> Subtract(const Complex &that) const {
RealFlags flags;
Part reDiff{re_.Subtract(that.re_).AccumulateFlags(flags)};
Part imDiff{im_.Subtract(that.im_).AccumulateFlags(flags)};
return {Complex{reDiff, imDiff}, flags};
}
constexpr ValueWithRealFlags<Complex> Multiply(const Complex &that) const {
// (a + ib)*(c + id) -> ac - bd + i(ad + bc)
RealFlags flags;
Part ac{re_.Multiply(that.re_).AccumulateFlags(flags)};
Part bd{im_.Multiply(that.im_).AccumulateFlags(flags)};
Part ad{re_.Multiply(that.im_).AccumulateFlags(flags)};
Part bc{im_.Multiply(that.re_).AccumulateFlags(flags)};
Part acbd{ac.Subtract(bd).AccumulateFlags(flags)};
Part adbc{ad.Add(bc).AccumulateFlags(flags)};
return {Complex{acbd, adbc}, flags};
}
constexpr ValueWithRealFlags<Complex> Divide(const Complex &that) const {
// (a + ib)/(c + id) -> [(a+ib)*(c-id)] / [(c+id)*(c-id)]
// -> [ac+bd+i(bc-ad)] / (cc+dd)
// -> ((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;
bool cGEd{that.re_.ABS().Compare(that.im_.ABS()) != Relation::Less};
if (cGEd) {
scale = that.im_.Divide(that.re_).AccumulateFlags(flags);
} else {
scale = that.re_.Divide(that.im_).AccumulateFlags(flags);
}
Part den;
if (cGEd) {
Part dS{scale.Multiply(that.im_).AccumulateFlags(flags)};
den = dS.Add(that.re_).AccumulateFlags(flags);
} else {
Part cS{scale.Multiply(that.re_).AccumulateFlags(flags)};
den = cS.Add(that.im_).AccumulateFlags(flags);
}
Part aS{scale.Multiply(re_).AccumulateFlags(flags)};
Part bS{scale.Multiply(im_).AccumulateFlags(flags)};
Part re1, im1;
if (cGEd) {
re1 = re_.Add(bS).AccumulateFlags(flags);
im1 = im_.Subtract(aS).AccumulateFlags(flags);
} else {
re1 = aS.Add(im_).AccumulateFlags(flags);
im1 = bS.Subtract(re_).AccumulateFlags(flags);
}
Part re{re1.Divide(den).AccumulateFlags(flags)};
Part im{im1.Divide(den).AccumulateFlags(flags)};
return {Complex{re, im}, flags};
}
private:
Part re_, im_;
};
extern template class Complex<Real<Integer<16>, 11>>;
extern template class Complex<Real<Integer<32>, 24>>;
extern template class Complex<Real<Integer<64>, 53>>;
extern template class Complex<Real<Integer<80>, 64, false>>;
extern template class Complex<Real<Integer<128>, 112>>;
} // namespace Fortran::evaluate::value
#endif // FORTRAN_EVALUATE_COMPLEX_H_

View File

@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include "complex.h"
#include "integer.h"
#include "logical.h"
#include "real.h"
@ -22,6 +23,7 @@ template class Integer<8>;
template class Integer<16>;
template class Integer<32>;
template class Integer<64>;
template class Integer<80>;
template class Integer<128>;
template class Real<Integer<16>, 11>;
@ -30,6 +32,12 @@ template class Real<Integer<64>, 53>;
template class Real<Integer<80>, 64, false>;
template class Real<Integer<128>, 112>;
template class Complex<Real<Integer<16>, 11>>;
template class Complex<Real<Integer<32>, 24>>;
template class Complex<Real<Integer<64>, 53>>;
template class Complex<Real<Integer<80>, 64, false>>;
template class Complex<Real<Integer<128>, 112>>;
template class Logical<8>;
template class Logical<16>;
template class Logical<32>;

View File

@ -844,6 +844,7 @@ extern template class Integer<8>;
extern template class Integer<16>;
extern template class Integer<32>;
extern template class Integer<64>;
extern template class Integer<80>;
extern template class Integer<128>;
} // namespace Fortran::evaluate::value
#endif // FORTRAN_EVALUATE_INTEGER_H_

View File

@ -19,6 +19,7 @@
// representation types in the evaluation library for ease of template
// programming.
#include "complex.h"
#include "integer.h"
#include "logical.h"
#include "real.h"
@ -39,19 +40,19 @@ template<> struct Real<2> {
static constexpr Classification classification{Classification::Real};
static constexpr int kind{2};
static constexpr bool hasLen{false};
using ValueType = value::Real<value::Integer<16>, 11>;
using ValueType = value::Real<typename Integer<kind>::ValueType, 11>;
};
template<> struct Real<4> {
static constexpr Classification classification{Classification::Real};
static constexpr int kind{4};
static constexpr bool hasLen{false};
using ValueType = value::Real<value::Integer<32>, 24>;
using ValueType = value::Real<typename Integer<kind>::ValueType, 24>;
};
template<> struct Real<8> {
static constexpr Classification classification{Classification::Real};
static constexpr int kind{8};
static constexpr bool hasLen{false};
using ValueType = value::Real<value::Integer<64>, 53>;
using ValueType = value::Real<typename Integer<kind>::ValueType, 53>;
};
template<> struct Real<10> {
static constexpr Classification classification{Classification::Real};
@ -63,17 +64,15 @@ template<> struct Real<16> {
static constexpr Classification classification{Classification::Real};
static constexpr int kind{16};
static constexpr bool hasLen{false};
using ValueType = value::Real<value::Integer<128>, 112>;
using ValueType = value::Real<typename Integer<kind>::ValueType, 112>;
};
#if 0 // TODO
template<int KIND> struct Complex {
static constexpr Classification classification{Classification::Complex};
static constexpr int kind{KIND};
static constexpr bool hasLen{false};
using ValueType = value::Complex<8 * kind>;
using ValueType = value::Complex<typename Real<(8 * kind / 2)>::ValueType>;
};
#endif
template<int KIND> struct Logical {
static constexpr Classification classification{Classification::Logical};
@ -100,9 +99,7 @@ template<int KIND> struct Character {
using DefaultReal = Real<4>;
using DefaultInteger = Integer<DefaultReal::kind>;
using IntrinsicTypeParameterType = DefaultInteger;
#if 0 // TODO
using DefaultComplex = Complex<2 * DefaultReal::kind>;
#endif
using DefaultLogical = Logical<DefaultReal::kind>;
#if 0 // TODO
using DefaultCharacter = Character<1>;

View File

@ -12,14 +12,20 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include "../../lib/evaluate/real.h"
#include "fp-testing.h"
#include "testing.h"
#include "../../lib/evaluate/integer.h"
#include "../../lib/evaluate/type.h"
#include <cstdio>
using namespace Fortran::evaluate;
using namespace Fortran::evaluate::value;
using Real2 = typename type::Real<2>::ValueType;
using Real4 = typename type::Real<4>::ValueType;
using Real8 = typename type::Real<8>::ValueType;
using Real10 = typename type::Real<10>::ValueType;
using Real16 = typename type::Real<16>::ValueType;
using Integer4 = typename type::Integer<4>::ValueType;
using Integer8 = typename type::Integer<8>::ValueType;
template<typename R> void basicTests(int rm, Rounding rounding) {
char desc[64];
@ -105,14 +111,14 @@ template<typename R> void basicTests(int rm, Rounding rounding) {
x <<= j;
std::snprintf(ldesc, sizeof ldesc, "%s j=%d x=0x%llx rm=%d", desc,
static_cast<int>(j), static_cast<unsigned long long>(x), rm);
Integer<64> ix{x};
Integer8 ix{x};
TEST(!ix.IsNegative())(ldesc);
MATCH(x, ix.ToUInt64())(ldesc);
vr = R::ConvertSigned(ix, rounding);
TEST(!vr.value.IsNegative())(ldesc);
TEST(!vr.value.IsNotANumber())(ldesc);
TEST(!vr.value.IsZero())(ldesc);
auto ivf = vr.value.template ToInteger<Integer<64>>();
auto ivf = vr.value.template ToInteger<Integer8>();
if (j > (maxExponent / 2)) {
TEST(vr.flags.test(RealFlag::Overflow))(ldesc);
TEST(vr.value.IsInfinite())(ldesc);
@ -134,7 +140,7 @@ template<typename R> void basicTests(int rm, Rounding rounding) {
TEST(vr.value.IsNegative())(ldesc);
TEST(!vr.value.IsNotANumber())(ldesc);
TEST(!vr.value.IsZero())(ldesc);
ivf = vr.value.template ToInteger<Integer<64>>();
ivf = vr.value.template ToInteger<Integer8>();
if (j > (maxExponent / 2)) {
TEST(vr.flags.test(RealFlag::Overflow))(ldesc);
TEST(vr.value.IsInfinite())(ldesc);
@ -191,8 +197,8 @@ void inttest(std::int64_t x, int pass, Rounding rounding) {
float f;
} u;
ScopedHostFloatingPointEnvironment fpenv;
Integer<64> ix{x};
ValueWithRealFlags<RealKind4> real;
Integer8 ix{x};
ValueWithRealFlags<Real4> real;
real = real.value.ConvertSigned(ix, rounding);
fpenv.ClearFlags();
float fcheck = x; // TODO unsigned too
@ -224,14 +230,14 @@ void subset32bit(int pass, Rounding rounding) {
std::uint32_t rj{MakeReal(j)};
u.u32 = rj;
float fj{u.f};
RealKind4 x{Integer<32>{std::uint64_t{rj}}};
Real4 x{Integer4{std::uint64_t{rj}}};
for (std::uint32_t k{0}; k < 8192; ++k) {
std::uint32_t rk{MakeReal(k)};
u.u32 = rk;
float fk{u.f};
RealKind4 y{Integer<32>{std::uint64_t{rk}}};
Real4 y{Integer4{std::uint64_t{rk}}};
{
ValueWithRealFlags<RealKind4> sum{x.Add(y, rounding)};
ValueWithRealFlags<Real4> sum{x.Add(y, rounding)};
fpenv.ClearFlags();
float fcheck{fj + fk};
auto actualFlags{FlagsToBits(fpenv.CurrentFlags())};
@ -243,7 +249,7 @@ void subset32bit(int pass, Rounding rounding) {
("%d 0x%x + 0x%x", pass, rj, rk);
}
{
ValueWithRealFlags<RealKind4> diff{x.Subtract(y, rounding)};
ValueWithRealFlags<Real4> diff{x.Subtract(y, rounding)};
fpenv.ClearFlags();
float fcheck{fj - fk};
auto actualFlags{FlagsToBits(fpenv.CurrentFlags())};
@ -255,7 +261,7 @@ void subset32bit(int pass, Rounding rounding) {
("%d 0x%x - 0x%x", pass, rj, rk);
}
{
ValueWithRealFlags<RealKind4> prod{x.Multiply(y, rounding)};
ValueWithRealFlags<Real4> prod{x.Multiply(y, rounding)};
fpenv.ClearFlags();
float fcheck{fj * fk};
auto actualFlags{FlagsToBits(fpenv.CurrentFlags())};
@ -267,7 +273,7 @@ void subset32bit(int pass, Rounding rounding) {
("%d 0x%x * 0x%x -> 0x%x", pass, rj, rk, rcheck);
}
{
ValueWithRealFlags<RealKind4> quot{x.Divide(y, rounding)};
ValueWithRealFlags<Real4> quot{x.Divide(y, rounding)};
fpenv.ClearFlags();
float fcheck{fj / fk};
auto actualFlags{FlagsToBits(fpenv.CurrentFlags())};
@ -283,11 +289,11 @@ void subset32bit(int pass, Rounding rounding) {
}
void roundTest(int rm, Rounding rounding) {
basicTests<RealKind2>(rm, rounding);
basicTests<RealKind4>(rm, rounding);
basicTests<RealKind8>(rm, rounding);
basicTests<RealKind10>(rm, rounding);
basicTests<RealKind16>(rm, rounding);
basicTests<Real2>(rm, rounding);
basicTests<Real4>(rm, rounding);
basicTests<Real8>(rm, rounding);
basicTests<Real10>(rm, rounding);
basicTests<Real16>(rm, rounding);
ScopedHostFloatingPointEnvironment::SetRounding(rounding);
subset32bit(rm, rounding);
}