2018-06-02 00:30:31 +02:00
|
|
|
// 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_REAL_H_
|
|
|
|
#define FORTRAN_EVALUATE_REAL_H_
|
|
|
|
|
|
|
|
#include "common.h"
|
|
|
|
#include "integer.h"
|
|
|
|
#include <cinttypes>
|
2018-06-04 18:49:47 +02:00
|
|
|
#include <limits>
|
2018-06-02 00:30:31 +02:00
|
|
|
|
|
|
|
namespace Fortran::evaluate {
|
|
|
|
|
2018-06-04 18:49:47 +02:00
|
|
|
// Model IEEE-754 floating-point numbers. The exponent range is that of a
|
|
|
|
// full int, and all significands are explicitly normalized.
|
2018-06-02 00:30:31 +02:00
|
|
|
// The class template parameter specifies the total number of bits in the
|
2018-06-04 18:49:47 +02:00
|
|
|
// significand, including the explicit greatest-order bit.
|
2018-06-05 00:56:40 +02:00
|
|
|
template<int PRECISION> class Real {
|
2018-06-02 00:30:31 +02:00
|
|
|
public:
|
2018-06-05 00:56:40 +02:00
|
|
|
static constexpr int precision{PRECISION};
|
|
|
|
static_assert(precision > 0);
|
2018-06-02 00:30:31 +02:00
|
|
|
|
2018-06-05 00:56:40 +02:00
|
|
|
template<typename A>
|
|
|
|
struct Result {
|
|
|
|
A value;
|
2018-06-04 18:49:47 +02:00
|
|
|
int flags{RealFlag::Ok};
|
|
|
|
};
|
|
|
|
|
2018-06-02 00:30:31 +02:00
|
|
|
constexpr Real() {} // +0.0
|
2018-06-04 18:49:47 +02:00
|
|
|
constexpr Real(const Real &) = default;
|
2018-06-02 00:30:31 +02:00
|
|
|
constexpr Real(std::int64_t n)
|
2018-06-05 00:56:40 +02:00
|
|
|
: significand_{n}, exponent_{precision}, negative_{n < 0} {
|
2018-06-02 00:30:31 +02:00
|
|
|
if (negative_) {
|
|
|
|
significand_ = significand_.Negate().value; // overflow is safe to ignore
|
|
|
|
}
|
|
|
|
Normalize();
|
|
|
|
}
|
|
|
|
|
2018-06-05 00:56:40 +02:00
|
|
|
// TODO conversion from (or to?) other real types
|
|
|
|
|
|
|
|
template<int b, int pb, typename p, typename dp, bool le>
|
|
|
|
constexpr Real(const Integer<b, pb, p, dp, le> &n, Rounding rounding = Rounding::TiesToEven)
|
|
|
|
: negative_{n.IsNegative()} {
|
|
|
|
if (negative_) {
|
|
|
|
n = n.Negate().value;
|
|
|
|
}
|
|
|
|
if (n.bits <= precision) {
|
|
|
|
exponent_ = precision;
|
|
|
|
significand_.Convert(n);
|
|
|
|
Normalize();
|
|
|
|
} else {
|
|
|
|
int lshift{n.LEADZ()};
|
|
|
|
exponent_ = n.bits - lshift;
|
|
|
|
int rshift{n.bits - (lshift + precision)};
|
|
|
|
if (rshift <= 0) {
|
|
|
|
significand_.Convert(n);
|
|
|
|
significand_ = significand_.SHIFTL(precision - exponent_);
|
|
|
|
} else {
|
|
|
|
RoundingBits roundingBits;
|
|
|
|
roundingBits.round = n.BTEST(rshift - 1);
|
|
|
|
roundingBits.guard = !n.SHIFTL(n.bits - rshift).IsZero();
|
|
|
|
significand_.Convert(n.SHIFTR(rshift));
|
|
|
|
Round(rounding, roundingBits);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
constexpr Real &operator=(const Real &) = default;
|
|
|
|
|
2018-06-02 00:30:31 +02:00
|
|
|
constexpr bool IsNegative() const { return negative_ && !notANumber_; }
|
|
|
|
constexpr bool IsFinite() const { return !infinite_ && !notANumber_; }
|
|
|
|
constexpr bool IsInfinite() const { return infinite_ && !notANumber_; }
|
|
|
|
constexpr bool IsANumber() const { return !notANumber_; }
|
|
|
|
constexpr bool IsNotANumber() const { return notANumber_; }
|
|
|
|
constexpr bool IsZero() const { return !notANumber_ && significand_.IsZero(); }
|
|
|
|
|
2018-06-05 00:56:40 +02:00
|
|
|
constexpr Real ABS() const { // non-arithmetic, no flags
|
|
|
|
Real result{*this};
|
|
|
|
result.negative_ = false;
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
constexpr DefaultIntrinsicInteger EXPONENT() const {
|
|
|
|
if (notANumber_ || infinite_) {
|
|
|
|
return DefaultIntrinsicInteger::HUGE();
|
|
|
|
} else {
|
|
|
|
return {std::int64_t{exponent_}};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static constexpr Real EPSILON() {
|
|
|
|
Real epsilon;
|
|
|
|
epsilon.exponent_ = -precision;
|
|
|
|
epsilon.significand_.IBSET(precision - 1);
|
|
|
|
return epsilon;
|
|
|
|
}
|
|
|
|
|
|
|
|
template<typename INT>
|
|
|
|
constexpr Result<INT> ToInteger() const {
|
|
|
|
Result<INT> result;
|
|
|
|
if (notANumber_) {
|
|
|
|
result.flags |= RealFlag::InvalidArgument;
|
|
|
|
result.value = result.value.HUGE();
|
|
|
|
} else if (infinite_ || exponent_ >= result.value.bits) {
|
|
|
|
if (negative_) {
|
|
|
|
result.value = result.value.MASKL(1);
|
|
|
|
} else {
|
|
|
|
result.value = result.value.HUGE();
|
|
|
|
}
|
|
|
|
result.flags = RealFlag::Overflow;
|
|
|
|
} else {
|
|
|
|
if (exponent_ > 0) {
|
|
|
|
result.value = INT::Convert(significand_.SHIFTR(result.value.bits - exponent_));
|
|
|
|
}
|
|
|
|
if (negative_) {
|
|
|
|
auto negated = result.value.Negate();
|
|
|
|
if (result.overflow) {
|
|
|
|
result.flags |= RealFlag::Overflow;
|
|
|
|
result.value = result.value.HUGE();
|
|
|
|
} else {
|
|
|
|
result.value = negated.value;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
2018-06-04 18:49:47 +02:00
|
|
|
|
2018-06-02 00:30:31 +02:00
|
|
|
constexpr Relation Compare(const Real &y) const {
|
|
|
|
if (notANumber_ || y.notANumber_) {
|
|
|
|
return Relation::Unordered;
|
|
|
|
} else if (infinite_) {
|
|
|
|
if (y.infinite_) {
|
|
|
|
if (negative_) {
|
|
|
|
return y.negative_ ? Relation::Equal : Relation::Less;
|
|
|
|
} else {
|
|
|
|
return y.negative_ ? Relation::Greater : Relation::Equal;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return negative_ ? Relation::Less : Relation::Greater;
|
|
|
|
}
|
|
|
|
} else if (y.infinite_) {
|
|
|
|
return y.negative_ ? Relation::Greater : Relation::Less;
|
|
|
|
} else {
|
|
|
|
// two finite numbers
|
|
|
|
if (exponent_ == y.exponent_) {
|
|
|
|
Ordering order{significand_.CompareUnsigned(y.significand_)};
|
|
|
|
if (order == Ordering::Equal) {
|
|
|
|
if (negative_ == y.negative_ ||
|
|
|
|
(exponent_ == 0 && significand_.IsZero())) {
|
|
|
|
// Ignore signs on zeros, +0.0 == -0.0
|
|
|
|
return Relation::Equal;
|
|
|
|
} else {
|
|
|
|
// finite nonzero numbers, same exponent & significand
|
|
|
|
return negative_ ? Relation::Less : Relation::Greater;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// finite numbers, same exponent, distinct significands
|
|
|
|
if (negative_ != y.negative_) {
|
|
|
|
return negative_ ? Relation::Less : Relation::Greater;
|
|
|
|
} else {
|
|
|
|
return RelationFromOrdering(order);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// not same exponent
|
|
|
|
if (negative_ != y.negative_) {
|
|
|
|
return negative_ ? Relation::Less : Relation::Greater;
|
|
|
|
} else {
|
|
|
|
return exponent_ < y.exponent_ ? Relation::Less : Relation::Greater;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-06-05 00:56:40 +02:00
|
|
|
constexpr Result<Real> Add(const Real &y, Rounding rounding) const {
|
|
|
|
Result<Real> result;
|
2018-06-04 18:49:47 +02:00
|
|
|
if (notANumber_ || y.notANumber_) {
|
|
|
|
result.value.notANumber_ = true; // NaN + x -> NaN
|
|
|
|
result.flags = RealFlag::InvalidArgument;
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
if (infinite_ || y.infinite_) {
|
|
|
|
if (negative_ == y.negative_) {
|
|
|
|
result.value.infinite_ = true; // +/-Inf + +/-Inf -> +/-Inf
|
|
|
|
result.value.negative_ = negative_;
|
|
|
|
} else {
|
|
|
|
result.value.notANumber_ = true; // +/-Inf + -/+Inf -> NaN
|
|
|
|
result.flags = RealFlag::InvalidArgument;
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
if (exponent_ < y.exponent_) {
|
|
|
|
// y is larger; simplify by reversing
|
|
|
|
return y.Add(*this, rounding);
|
|
|
|
}
|
|
|
|
if (exponent_ == y.exponent_ &&
|
|
|
|
negative_ != y.negative_ &&
|
|
|
|
significand_.CompareUnsigned(y.significand_) == Ordering::Less) {
|
|
|
|
// Same exponent, opposite signs, and y is larger.
|
|
|
|
result = y.Add(*this, rounding);
|
2018-06-05 00:56:40 +02:00
|
|
|
result.value.negative_ ^= true;
|
2018-06-04 18:49:47 +02:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
// exponent is greater than or equal to y's
|
|
|
|
result.value = y;
|
|
|
|
result.value.exponent_ = exponent_;
|
|
|
|
result.value.negative_ = negative_;
|
|
|
|
RoundingBits roundingBits{result.value.ShiftSignificandRight(exponent_ - y.exponent_)};
|
|
|
|
if (negative_ != y.negative_) {
|
2018-06-05 00:56:40 +02:00
|
|
|
typename Significand::ValueWithOverflow negated{result.value.significand_.Negate()};
|
2018-06-04 18:49:47 +02:00
|
|
|
if (negated.overflow) {
|
|
|
|
// y had only its MSB set. Result is our significand, less its MSB.
|
2018-06-05 00:56:40 +02:00
|
|
|
result.value.significand_ = significand_.IBCLR(precision - 1);
|
2018-06-04 18:49:47 +02:00
|
|
|
} else {
|
2018-06-05 00:56:40 +02:00
|
|
|
typename Significand::ValueWithCarry diff{significand_.AddUnsigned(negated.value)};
|
|
|
|
result.value.significand_ = diff.value;
|
2018-06-04 18:49:47 +02:00
|
|
|
}
|
|
|
|
} else {
|
2018-06-05 00:56:40 +02:00
|
|
|
typename Significand::ValueWithCarry sum{significand_.AddUnsigned(result.value.significand_)};
|
2018-06-04 18:49:47 +02:00
|
|
|
if (sum.carry) {
|
|
|
|
roundingBits.guard |= roundingBits.round;
|
|
|
|
roundingBits.round = sum.value.BTEST(0);
|
2018-06-05 00:56:40 +02:00
|
|
|
result.value.significand_ = sum.value.SHIFTR(1).IBSET(precision - 1);
|
2018-06-04 18:49:47 +02:00
|
|
|
++result.value.exponent_;
|
|
|
|
} else {
|
|
|
|
result.value.significand_ = sum.value;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
result.value.Round(rounding, roundingBits);
|
|
|
|
result.flags |= result.value.Normalize();
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2018-06-05 00:56:40 +02:00
|
|
|
constexpr Result<Real> Subtract(const Real &y, Rounding rounding) const {
|
2018-06-04 18:49:47 +02:00
|
|
|
Real minusy{y};
|
|
|
|
minusy.negative_ ^= true;
|
|
|
|
return Add(minusy, rounding);
|
|
|
|
}
|
|
|
|
|
2018-06-05 00:56:40 +02:00
|
|
|
constexpr Result<Real> Multiply(const Real &y, Rounding rounding) const {
|
|
|
|
Result<Real> result;
|
|
|
|
if (notANumber_ || y.notANumber_) {
|
|
|
|
result.value.notANumber_ = true; // NaN * x -> NaN
|
|
|
|
result.flags = RealFlag::InvalidArgument;
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
result.value.negative_ = negative_ != y.negative_;
|
|
|
|
if (infinite_ || y.infinite_) {
|
|
|
|
result.value.infinite_ = true;
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
typename Significand::Product product{significand_.MultiplyUnsigned(y.significand_)};
|
|
|
|
result.value.exponent_ = exponent_ + y.exponent_ - 1;
|
|
|
|
result.value.significand_ = product.upper;
|
|
|
|
RoundingBits roundingBits;
|
|
|
|
roundingBits.round = product.lower.BTEST(precision - 1);
|
|
|
|
roundingBits.guard = !product.lower.IBCLR(precision - 1).IsZero();
|
|
|
|
result.value.Round(rounding, roundingBits);
|
|
|
|
result.flags |= result.value.Normalize();
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
constexpr Result<Real> Divide(const Real &y, Rounding rounding) const {
|
|
|
|
Result<Real> result;
|
2018-06-04 18:49:47 +02:00
|
|
|
if (notANumber_ || y.notANumber_) {
|
|
|
|
result.value.notANumber_ = true; // NaN * x -> NaN
|
|
|
|
result.flags = RealFlag::InvalidArgument;
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
if (infinite_ || y.infinite_) {
|
|
|
|
result.value.infinite_ = true;
|
|
|
|
result.value.negative_ = negative_ != y.negative_;
|
|
|
|
return result;
|
|
|
|
}
|
2018-06-05 00:56:40 +02:00
|
|
|
result.value.negative_ = negative_ != y.negative_;
|
|
|
|
typename Significand::QuotientWithRemainder divided{significand_.DivideUnsigned(y.significand_)};
|
|
|
|
if (divided.divisionByZero) {
|
|
|
|
result.value.infinite_ = true;
|
|
|
|
result.flags |= RealFlag::DivideByZero;
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
result.value.exponent_ = exponent_ - y.exponent_ + 1;
|
|
|
|
result.value.significand_ = divided.quotient;
|
|
|
|
// To round, double the remainder and compare it to the divisor.
|
|
|
|
RoundingBits roundingBits;
|
|
|
|
typename Significand::ValueWithCarry doubledRem{divided.remainder.AddUnsigned(divided.remainder)};
|
|
|
|
Ordering drcmp{doubledRem.value.CompareUnsigned(y.significand_)};
|
|
|
|
roundingBits.round = drcmp != Ordering::Less;
|
|
|
|
roundingBits.guard = drcmp != Ordering::Equal;
|
|
|
|
result.value.Round(rounding, roundingBits);
|
|
|
|
result.flags |= result.value.Normalize();
|
|
|
|
return result;
|
2018-06-04 18:49:47 +02:00
|
|
|
}
|
|
|
|
|
2018-06-02 00:30:31 +02:00
|
|
|
private:
|
2018-06-05 00:56:40 +02:00
|
|
|
using Significand = Integer<precision>;
|
2018-06-04 18:49:47 +02:00
|
|
|
static constexpr int maxExponent{std::numeric_limits<int>::max() / 2};
|
|
|
|
static constexpr int minExponent{-maxExponent};
|
|
|
|
|
|
|
|
struct RoundingBits {
|
|
|
|
RoundingBits() {}
|
|
|
|
RoundingBits(const RoundingBits &) = default;
|
2018-06-05 00:56:40 +02:00
|
|
|
RoundingBits &operator=(const RoundingBits &) = default;
|
2018-06-04 18:49:47 +02:00
|
|
|
bool round{false};
|
|
|
|
bool guard{false}; // a/k/a "sticky" bit
|
|
|
|
};
|
2018-06-02 00:30:31 +02:00
|
|
|
|
|
|
|
// All values are normalized on output and assumed normal on input.
|
2018-06-04 18:49:47 +02:00
|
|
|
// Returns flag bits.
|
|
|
|
int Normalize() {
|
|
|
|
if (notANumber_) {
|
|
|
|
return RealFlag::InvalidArgument;
|
|
|
|
} else if (infinite_) {
|
|
|
|
return RealFlag::Ok;
|
|
|
|
} else {
|
2018-06-02 00:30:31 +02:00
|
|
|
int shift{significand_.LEADZ()};
|
2018-06-05 00:56:40 +02:00
|
|
|
if (shift >= precision) {
|
2018-06-02 00:30:31 +02:00
|
|
|
exponent_ = 0; // +/-0.0
|
2018-06-04 18:49:47 +02:00
|
|
|
return RealFlag::Ok;
|
2018-06-02 00:30:31 +02:00
|
|
|
} else {
|
|
|
|
exponent_ -= shift;
|
2018-06-04 18:49:47 +02:00
|
|
|
if (exponent_ < minExponent) {
|
|
|
|
exponent_ = 0;
|
|
|
|
significand_ = Significand{};
|
|
|
|
return RealFlag::Underflow;
|
|
|
|
} else if (exponent_ > maxExponent) {
|
|
|
|
infinite_ = true;
|
|
|
|
return RealFlag::Overflow;
|
|
|
|
} else {
|
|
|
|
if (shift > 0) {
|
|
|
|
significand_ = significand_.SHIFTL(shift);
|
|
|
|
}
|
|
|
|
return RealFlag::Ok;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
constexpr bool MustRound(Rounding rounding, const RoundingBits &bits) const {
|
2018-06-05 00:56:40 +02:00
|
|
|
bool round{false}; // to dodge bogus g++ warning about missing return
|
2018-06-04 18:49:47 +02:00
|
|
|
switch (rounding) {
|
|
|
|
case Rounding::TiesToEven:
|
2018-06-05 00:56:40 +02:00
|
|
|
round = bits.round && !bits.guard && significand_.BTEST(0);
|
|
|
|
break;
|
2018-06-04 18:49:47 +02:00
|
|
|
case Rounding::ToZero:
|
2018-06-05 00:56:40 +02:00
|
|
|
break;
|
2018-06-04 18:49:47 +02:00
|
|
|
case Rounding::Down:
|
2018-06-05 00:56:40 +02:00
|
|
|
round = negative_ && (bits.round || bits.guard);
|
|
|
|
break;
|
2018-06-04 18:49:47 +02:00
|
|
|
case Rounding::Up:
|
2018-06-05 00:56:40 +02:00
|
|
|
round = !negative_ && (bits.round || bits.guard);
|
|
|
|
break;
|
2018-06-04 18:49:47 +02:00
|
|
|
case Rounding::TiesAwayFromZero:
|
2018-06-05 00:56:40 +02:00
|
|
|
round = bits.round && !bits.guard;
|
|
|
|
break;
|
2018-06-04 18:49:47 +02:00
|
|
|
}
|
2018-06-05 00:56:40 +02:00
|
|
|
return round;
|
2018-06-04 18:49:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void Round(Rounding rounding, const RoundingBits &bits) {
|
|
|
|
if (MustRound(rounding, bits)) {
|
2018-06-05 00:56:40 +02:00
|
|
|
typename Significand::ValueWithCarry sum{significand_.AddUnsigned(Significand{}, true)};
|
2018-06-04 18:49:47 +02:00
|
|
|
if (sum.carry) {
|
|
|
|
// significand was all ones, and we rounded
|
|
|
|
++exponent_;
|
2018-06-05 00:56:40 +02:00
|
|
|
significand_ = sum.value.SHIFTR(1).IBSET(precision - 1);
|
2018-06-04 18:49:47 +02:00
|
|
|
} else {
|
|
|
|
significand_ = sum.value;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
RoundingBits ShiftSignificandRight(int places) {
|
|
|
|
RoundingBits result;
|
2018-06-05 00:56:40 +02:00
|
|
|
if (places > significand_.bits) {
|
2018-06-04 18:49:47 +02:00
|
|
|
result.guard = !significand_.IsZero();
|
2018-06-05 00:56:40 +02:00
|
|
|
significand_ = Significand{};
|
2018-06-04 18:49:47 +02:00
|
|
|
} else if (places > 0) {
|
|
|
|
if (places > 1) {
|
|
|
|
result.guard = significand_.TRAILZ() + 1 < places;
|
2018-06-02 00:30:31 +02:00
|
|
|
}
|
2018-06-04 18:49:47 +02:00
|
|
|
result.round = significand_.BTEST(places - 1);
|
|
|
|
significand_ = significand_.SHIFTR(places);
|
2018-06-02 00:30:31 +02:00
|
|
|
}
|
2018-06-04 18:49:47 +02:00
|
|
|
return result;
|
2018-06-02 00:30:31 +02:00
|
|
|
}
|
|
|
|
|
2018-06-05 00:56:40 +02:00
|
|
|
Significand significand_{}; // all bits explicit
|
2018-06-02 00:30:31 +02:00
|
|
|
int exponent_{0}; // unbiased; 1.0 has exponent 1
|
|
|
|
bool negative_{false};
|
|
|
|
bool infinite_{false};
|
|
|
|
bool notANumber_{false};
|
|
|
|
};
|
|
|
|
|
2018-06-05 00:56:40 +02:00
|
|
|
extern template class Real<11>;
|
|
|
|
extern template class Real<24>;
|
|
|
|
extern template class Real<53>;
|
|
|
|
extern template class Real<112>;
|
|
|
|
|
2018-06-02 00:30:31 +02:00
|
|
|
} // namespace Fortran::evaluate
|
|
|
|
#endif // FORTRAN_EVALUATE_REAL_H_
|