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-05 22:55:56 +02:00
|
|
|
// Models IEEE-754 floating-point numbers. The first argument to this
|
|
|
|
// class template must be (or look like) an instance of Integer.
|
|
|
|
template<typename WORD, int PRECISION, bool IMPLICIT_MSB = true> class Real {
|
2018-06-02 00:30:31 +02:00
|
|
|
public:
|
2018-06-05 22:55:56 +02:00
|
|
|
using Word = WORD;
|
|
|
|
static constexpr int bits{Word::bits};
|
2018-06-05 00:56:40 +02:00
|
|
|
static constexpr int precision{PRECISION};
|
2018-06-05 22:55:56 +02:00
|
|
|
static constexpr bool implicitMSB{IMPLICIT_MSB};
|
|
|
|
static constexpr int significandBits{precision - implicitMSB};
|
|
|
|
static constexpr int exponentBits{bits - significandBits - 1 /*sign*/};
|
2018-06-05 00:56:40 +02:00
|
|
|
static_assert(precision > 0);
|
2018-06-05 22:55:56 +02:00
|
|
|
static_assert(exponentBits > 1);
|
|
|
|
static constexpr std::uint64_t maxExponent{(1 << exponentBits) - 1};
|
|
|
|
static constexpr std::uint64_t exponentBias{maxExponent / 2};
|
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-06 01:29:26 +02:00
|
|
|
constexpr Real(const Word &bits) : word_{bits} {}
|
2018-06-06 20:48:00 +02:00
|
|
|
constexpr Real &operator=(const Real &) = default;
|
2018-06-02 00:30:31 +02:00
|
|
|
|
2018-06-05 22:55:56 +02:00
|
|
|
template<typename INT>
|
|
|
|
static constexpr ValueWithRealFlags<Real> ConvertSigned(
|
|
|
|
const INT &n, Rounding rounding = Rounding::TiesToEven) {
|
|
|
|
bool isNegative{n.IsNegative()};
|
2018-06-06 01:29:26 +02:00
|
|
|
INT absN{n};
|
2018-06-05 22:55:56 +02:00
|
|
|
if (isNegative) {
|
2018-06-06 01:29:26 +02:00
|
|
|
absN = n.Negate().value; // overflow is safe to ignore
|
2018-06-05 00:56:40 +02:00
|
|
|
}
|
2018-06-06 01:29:26 +02:00
|
|
|
int leadz{absN.LEADZ()};
|
|
|
|
if (leadz >= absN.bits) {
|
2018-06-05 22:55:56 +02:00
|
|
|
return {}; // all bits zero -> +0.0
|
|
|
|
}
|
|
|
|
ValueWithRealFlags<Real> result;
|
2018-06-06 01:29:26 +02:00
|
|
|
std::uint64_t exponent{exponentBias + absN.bits - leadz - 1};
|
|
|
|
int bitsNeeded{absN.bits - (leadz + implicitMSB)};
|
2018-06-05 22:55:56 +02:00
|
|
|
int bitsLost{bitsNeeded - significandBits};
|
|
|
|
if (bitsLost <= 0) {
|
2018-06-06 01:29:26 +02:00
|
|
|
Fraction fraction{Fraction::Convert(absN).value};
|
2018-06-05 22:55:56 +02:00
|
|
|
result.flags |= result.value.Normalize(
|
|
|
|
isNegative, exponent, fraction.SHIFTL(-bitsLost));
|
2018-06-05 00:56:40 +02:00
|
|
|
} else {
|
2018-06-06 01:29:26 +02:00
|
|
|
Fraction fraction{Fraction::Convert(absN.SHIFTR(bitsLost)).value};
|
2018-06-05 22:55:56 +02:00
|
|
|
result.flags |= result.value.Normalize(isNegative, exponent, fraction);
|
2018-06-08 19:58:58 +02:00
|
|
|
RoundingBits roundingBits{absN, bitsLost};
|
2018-06-05 22:55:56 +02:00
|
|
|
result.flags |= result.value.Round(rounding, roundingBits);
|
2018-06-05 00:56:40 +02:00
|
|
|
}
|
2018-06-05 22:55:56 +02:00
|
|
|
return result;
|
2018-06-05 00:56:40 +02:00
|
|
|
}
|
|
|
|
|
2018-06-05 22:55:56 +02:00
|
|
|
// TODO conversion from (or to?) (other) real types
|
2018-06-05 01:22:58 +02:00
|
|
|
// TODO AINT/ANINT, CEILING, FLOOR, DIM, MAX, MIN, DPROD, FRACTION
|
|
|
|
// HUGE, INT/NINT, MAXEXPONENT, MINEXPONENT, NEAREST, OUT_OF_RANGE,
|
|
|
|
// PRECISION, HUGE, TINY, RRSPACING/SPACING, SCALE, SET_EXPONENT, SIGN
|
|
|
|
|
2018-06-06 20:48:00 +02:00
|
|
|
constexpr Word RawBits() const { return word_; }
|
2018-06-05 22:55:56 +02:00
|
|
|
constexpr std::uint64_t Exponent() const {
|
|
|
|
return word_.IBITS(significandBits, exponentBits).ToUInt64();
|
|
|
|
}
|
2018-06-06 01:29:26 +02:00
|
|
|
constexpr bool IsNegative() const {
|
|
|
|
return !IsNotANumber() && word_.BTEST(bits - 1);
|
|
|
|
}
|
2018-06-05 22:55:56 +02:00
|
|
|
constexpr bool IsNotANumber() const {
|
|
|
|
return Exponent() == maxExponent && !GetSignificand().IsZero();
|
|
|
|
}
|
|
|
|
constexpr bool IsInfinite() const {
|
|
|
|
return Exponent() == maxExponent && GetSignificand().IsZero();
|
|
|
|
}
|
2018-06-05 01:22:58 +02:00
|
|
|
constexpr bool IsZero() const {
|
2018-06-05 22:55:56 +02:00
|
|
|
return Exponent() == 0 && GetSignificand().IsZero();
|
|
|
|
}
|
2018-06-08 19:24:15 +02:00
|
|
|
constexpr bool IsDenormal() const {
|
|
|
|
return Exponent() == 0 && !GetSignificand().IsZero();
|
|
|
|
}
|
2018-06-05 22:55:56 +02:00
|
|
|
|
|
|
|
constexpr Real ABS() const { // non-arithmetic, no flags returned
|
|
|
|
Real result;
|
|
|
|
result.word_ = word_.IBCLR(bits - 1);
|
|
|
|
return result;
|
2018-06-05 01:22:58 +02:00
|
|
|
}
|
2018-06-02 00:30:31 +02:00
|
|
|
|
2018-06-05 22:55:56 +02:00
|
|
|
constexpr Real Negate() const {
|
|
|
|
Real result;
|
|
|
|
result.word_ = word_.IEOR(word_.MASKL(1));
|
2018-06-05 00:56:40 +02:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
constexpr DefaultIntrinsicInteger EXPONENT() const {
|
2018-06-05 22:55:56 +02:00
|
|
|
std::uint64_t exponent{Exponent()};
|
|
|
|
if (exponent == maxExponent) {
|
2018-06-05 00:56:40 +02:00
|
|
|
return DefaultIntrinsicInteger::HUGE();
|
|
|
|
} else {
|
2018-06-05 22:55:56 +02:00
|
|
|
return {static_cast<std::int64_t>(exponent - exponentBias)};
|
2018-06-05 00:56:40 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static constexpr Real EPSILON() {
|
|
|
|
Real epsilon;
|
2018-06-05 22:55:56 +02:00
|
|
|
epsilon.Normalize(false, exponentBias - precision, Fraction::MASKL(1));
|
2018-06-05 00:56:40 +02:00
|
|
|
return epsilon;
|
|
|
|
}
|
|
|
|
|
2018-06-05 01:22:58 +02:00
|
|
|
template<typename INT> constexpr ValueWithRealFlags<INT> ToInteger() const {
|
2018-06-05 22:55:56 +02:00
|
|
|
bool isNegative{IsNegative()};
|
|
|
|
std::uint64_t exponent{Exponent()};
|
|
|
|
Fraction fraction{GetFraction()};
|
2018-06-06 20:26:00 +02:00
|
|
|
ValueWithRealFlags<INT> result;
|
2018-06-05 22:55:56 +02:00
|
|
|
if (exponent == maxExponent && !fraction.IsZero()) { // NaN
|
2018-06-06 20:11:35 +02:00
|
|
|
result.flags.set(RealFlag::InvalidArgument);
|
2018-06-05 00:56:40 +02:00
|
|
|
result.value = result.value.HUGE();
|
2018-06-06 20:26:00 +02:00
|
|
|
} else if (exponent >= maxExponent || // +/-Inf
|
2018-06-06 20:48:00 +02:00
|
|
|
exponent >= exponentBias + result.value.bits) { // too big
|
2018-06-05 22:55:56 +02:00
|
|
|
if (isNegative) {
|
2018-06-05 00:56:40 +02:00
|
|
|
result.value = result.value.MASKL(1);
|
|
|
|
} else {
|
|
|
|
result.value = result.value.HUGE();
|
|
|
|
}
|
2018-06-06 20:11:35 +02:00
|
|
|
result.flags.set(RealFlag::Overflow);
|
2018-06-06 20:26:00 +02:00
|
|
|
} else if (exponent < exponentBias) { // |x| < 1.0 -> 0
|
2018-06-05 22:55:56 +02:00
|
|
|
if (!fraction.IsZero()) {
|
2018-06-06 20:11:35 +02:00
|
|
|
result.flags.set(RealFlag::Underflow);
|
|
|
|
result.flags.set(RealFlag::Inexact);
|
2018-06-05 22:55:56 +02:00
|
|
|
}
|
2018-06-05 00:56:40 +02:00
|
|
|
} else {
|
2018-06-06 20:26:00 +02:00
|
|
|
// finite number |x| >= 1.0
|
|
|
|
constexpr std::uint64_t noShiftExponent{exponentBias + precision - 1};
|
|
|
|
if (exponent < noShiftExponent) {
|
|
|
|
int rshift = noShiftExponent - exponent;
|
2018-06-05 22:55:56 +02:00
|
|
|
if (!fraction.IBITS(0, rshift).IsZero()) {
|
2018-06-06 20:11:35 +02:00
|
|
|
result.flags.set(RealFlag::Inexact);
|
2018-06-05 22:55:56 +02:00
|
|
|
}
|
|
|
|
auto truncated = result.value.Convert(fraction.SHIFTR(rshift));
|
|
|
|
if (truncated.overflow) {
|
2018-06-06 20:11:35 +02:00
|
|
|
result.flags.set(RealFlag::Overflow);
|
2018-06-05 22:55:56 +02:00
|
|
|
} else {
|
|
|
|
result.value = truncated.value;
|
|
|
|
}
|
|
|
|
} else {
|
2018-06-06 20:26:00 +02:00
|
|
|
int lshift = exponent - noShiftExponent;
|
2018-06-05 22:55:56 +02:00
|
|
|
if (lshift + precision >= result.value.bits) {
|
2018-06-06 20:11:35 +02:00
|
|
|
result.flags.set(RealFlag::Overflow);
|
2018-06-05 22:55:56 +02:00
|
|
|
} else {
|
|
|
|
result.value = result.value.Convert(fraction).value.SHIFTL(lshift);
|
|
|
|
}
|
2018-06-05 00:56:40 +02:00
|
|
|
}
|
2018-06-06 20:11:35 +02:00
|
|
|
if (result.flags.test(RealFlag::Overflow)) {
|
2018-06-05 22:55:56 +02:00
|
|
|
result.value = result.value.HUGE();
|
|
|
|
} else if (isNegative) {
|
2018-06-05 00:56:40 +02:00
|
|
|
auto negated = result.value.Negate();
|
2018-06-06 01:29:26 +02:00
|
|
|
if (negated.overflow) {
|
2018-06-06 20:11:35 +02:00
|
|
|
result.flags.set(RealFlag::Overflow);
|
2018-06-05 00:56:40 +02:00
|
|
|
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 {
|
2018-06-05 22:55:56 +02:00
|
|
|
if (IsNotANumber() || y.IsNotANumber()) { // NaN vs x, x vs NaN
|
2018-06-02 00:30:31 +02:00
|
|
|
return Relation::Unordered;
|
2018-06-05 22:55:56 +02:00
|
|
|
} else if (IsInfinite()) {
|
|
|
|
if (y.IsInfinite()) {
|
|
|
|
if (IsNegative()) { // -Inf vs +/-Inf
|
|
|
|
return y.IsNegative() ? Relation::Equal : Relation::Less;
|
|
|
|
} else { // +Inf vs +/-Inf
|
|
|
|
return y.IsNegative() ? Relation::Greater : Relation::Equal;
|
2018-06-02 00:30:31 +02:00
|
|
|
}
|
2018-06-05 22:55:56 +02:00
|
|
|
} else { // +/-Inf vs finite
|
|
|
|
return IsNegative() ? Relation::Less : Relation::Greater;
|
2018-06-02 00:30:31 +02:00
|
|
|
}
|
2018-06-05 22:55:56 +02:00
|
|
|
} else if (y.IsInfinite()) { // finite vs +/-Inf
|
|
|
|
return y.IsNegative() ? Relation::Greater : Relation::Less;
|
|
|
|
} else { // two finite numbers
|
|
|
|
bool isNegative{IsNegative()};
|
|
|
|
if (isNegative != y.IsNegative()) {
|
|
|
|
if (word_.IOR(y.word_).IBCLR(bits - 1).IsZero()) {
|
|
|
|
return Relation::Equal; // +/-0.0 == -/+0.0
|
2018-06-02 00:30:31 +02:00
|
|
|
} else {
|
2018-06-05 22:55:56 +02:00
|
|
|
return isNegative ? Relation::Less : Relation::Greater;
|
2018-06-02 00:30:31 +02:00
|
|
|
}
|
|
|
|
} else {
|
2018-06-05 22:55:56 +02:00
|
|
|
// same sign
|
|
|
|
Ordering order{CompareUnsigned(Exponent(), y.Exponent())};
|
|
|
|
if (order == Ordering::Equal) {
|
|
|
|
order = GetSignificand().CompareUnsigned(y.GetSignificand());
|
|
|
|
}
|
|
|
|
if (isNegative) {
|
|
|
|
order = Reverse(order);
|
2018-06-02 00:30:31 +02:00
|
|
|
}
|
2018-06-05 22:55:56 +02:00
|
|
|
return RelationFromOrdering(order);
|
2018-06-02 00:30:31 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-06-05 01:22:58 +02:00
|
|
|
constexpr ValueWithRealFlags<Real> Add(
|
2018-06-08 19:24:15 +02:00
|
|
|
const Real &y, Rounding rounding = Rounding::TiesToEven) const {
|
2018-06-05 01:22:58 +02:00
|
|
|
ValueWithRealFlags<Real> result;
|
2018-06-05 22:55:56 +02:00
|
|
|
if (IsNotANumber() || y.IsNotANumber()) {
|
|
|
|
result.value.word_ = NaNWord(); // NaN + x -> NaN
|
2018-06-06 20:11:35 +02:00
|
|
|
result.flags.set(RealFlag::InvalidArgument);
|
2018-06-04 18:49:47 +02:00
|
|
|
return result;
|
|
|
|
}
|
2018-06-05 22:55:56 +02:00
|
|
|
bool isNegative{IsNegative()};
|
|
|
|
bool yIsNegative{y.IsNegative()};
|
2018-06-09 00:18:03 +02:00
|
|
|
if (IsInfinite()) {
|
|
|
|
if (y.IsInfinite()) {
|
|
|
|
if (isNegative == yIsNegative) {
|
|
|
|
result.value = *this; // +/-Inf + +/-Inf -> +/-Inf
|
|
|
|
} else {
|
|
|
|
result.value.word_ = NaNWord(); // +/-Inf + -/+Inf -> NaN
|
|
|
|
result.flags.set(RealFlag::InvalidArgument);
|
|
|
|
}
|
2018-06-04 18:49:47 +02:00
|
|
|
} else {
|
2018-06-09 00:18:03 +02:00
|
|
|
result.value = *this; // +/-Inf + x -> +/-Inf
|
2018-06-04 18:49:47 +02:00
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
2018-06-09 00:18:03 +02:00
|
|
|
if (y.IsInfinite()) {
|
|
|
|
result.value = y; // x + +/-Inf -> +/-Inf
|
|
|
|
return result;
|
|
|
|
}
|
2018-06-05 22:55:56 +02:00
|
|
|
std::uint64_t exponent{Exponent()};
|
|
|
|
std::uint64_t yExponent{y.Exponent()};
|
|
|
|
if (exponent < yExponent) {
|
|
|
|
// y is larger in magnitude; simplify by reversing operands
|
2018-06-04 18:49:47 +02:00
|
|
|
return y.Add(*this, rounding);
|
|
|
|
}
|
2018-06-08 19:24:15 +02:00
|
|
|
if (exponent == yExponent && isNegative != yIsNegative) {
|
|
|
|
Ordering order{GetSignificand().CompareUnsigned(y.GetSignificand())};
|
|
|
|
if (order == Ordering::Less) {
|
|
|
|
// Same exponent, opposite signs, and y is larger in magnitude
|
|
|
|
return y.Add(*this, rounding);
|
|
|
|
}
|
|
|
|
if (order == Ordering::Equal) {
|
|
|
|
// x + (-x) -> +0.0, never -0.0
|
|
|
|
return {};
|
|
|
|
}
|
2018-06-04 18:49:47 +02:00
|
|
|
}
|
2018-06-05 22:55:56 +02:00
|
|
|
// Our exponent is greater than y's, or the exponents match and y is not
|
|
|
|
// of the opposite sign and greater magnitude. So (x+y) will have the
|
|
|
|
// same sign as x.
|
|
|
|
Fraction yFraction{y.GetFraction()};
|
|
|
|
Fraction fraction{GetFraction()};
|
2018-06-08 19:24:15 +02:00
|
|
|
int rshift = exponent - yExponent;
|
|
|
|
if (exponent > 0 && yExponent == 0) {
|
|
|
|
--rshift; // correct overshift when only y is denormal
|
|
|
|
}
|
2018-06-08 19:58:58 +02:00
|
|
|
RoundingBits roundingBits{yFraction, rshift};
|
2018-06-08 19:24:15 +02:00
|
|
|
yFraction = yFraction.SHIFTR(rshift);
|
|
|
|
bool carry{false};
|
2018-06-05 22:55:56 +02:00
|
|
|
if (isNegative != yIsNegative) {
|
2018-06-08 19:58:58 +02:00
|
|
|
// Opposite signs: subtract via addition of two's complement of y and
|
|
|
|
// the rounding bits.
|
2018-06-08 19:24:15 +02:00
|
|
|
yFraction = yFraction.NOT();
|
2018-06-08 19:58:58 +02:00
|
|
|
carry = roundingBits.Negate();
|
2018-06-08 19:24:15 +02:00
|
|
|
}
|
|
|
|
auto sum = fraction.AddUnsigned(yFraction, carry);
|
|
|
|
fraction = sum.value;
|
|
|
|
if (isNegative == yIsNegative && sum.carry) {
|
2018-06-08 19:58:58 +02:00
|
|
|
roundingBits.ShiftRight(sum.value.BTEST(0));
|
2018-06-09 00:18:03 +02:00
|
|
|
fraction = fraction.SHIFTR(1).IBSET(fraction.bits - 1);
|
2018-06-08 19:24:15 +02:00
|
|
|
++exponent;
|
2018-06-04 18:49:47 +02:00
|
|
|
}
|
2018-06-08 19:24:15 +02:00
|
|
|
result.flags |=
|
|
|
|
result.value.Normalize(isNegative, exponent, fraction, &roundingBits);
|
2018-06-05 22:55:56 +02:00
|
|
|
result.flags |= result.value.Round(rounding, roundingBits);
|
2018-06-04 18:49:47 +02:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2018-06-05 01:22:58 +02:00
|
|
|
constexpr ValueWithRealFlags<Real> Subtract(
|
2018-06-08 19:24:15 +02:00
|
|
|
const Real &y, Rounding rounding = Rounding::TiesToEven) const {
|
2018-06-05 22:55:56 +02:00
|
|
|
return Add(y.Negate(), rounding);
|
2018-06-04 18:49:47 +02:00
|
|
|
}
|
|
|
|
|
2018-06-05 01:22:58 +02:00
|
|
|
constexpr ValueWithRealFlags<Real> Multiply(
|
2018-06-08 19:24:15 +02:00
|
|
|
const Real &y, Rounding rounding = Rounding::TiesToEven) const {
|
2018-06-05 01:22:58 +02:00
|
|
|
ValueWithRealFlags<Real> result;
|
2018-06-05 22:55:56 +02:00
|
|
|
if (IsNotANumber() || y.IsNotANumber()) {
|
|
|
|
result.value.word_ = NaNWord(); // NaN * x -> NaN
|
2018-06-06 20:11:35 +02:00
|
|
|
result.flags.set(RealFlag::InvalidArgument);
|
2018-06-05 22:55:56 +02:00
|
|
|
} else {
|
|
|
|
bool isNegative{IsNegative() != y.IsNegative()};
|
|
|
|
if (IsInfinite() || y.IsInfinite()) {
|
2018-06-09 00:18:03 +02:00
|
|
|
if (IsZero() || y.IsZero()) {
|
|
|
|
result.value.word_ = NaNWord(); // 0 * Inf -> NaN
|
|
|
|
result.flags.set(RealFlag::InvalidArgument);
|
|
|
|
} else {
|
|
|
|
result.value.Normalize(isNegative, maxExponent, Fraction{});
|
|
|
|
}
|
2018-06-05 22:55:56 +02:00
|
|
|
} else {
|
|
|
|
auto product = GetFraction().MultiplyUnsigned(y.GetFraction());
|
2018-06-09 00:18:03 +02:00
|
|
|
std::int64_t exponent = Exponent(), yExponent = y.Exponent();
|
|
|
|
// A zero exponent field value has the same weight as 1.
|
|
|
|
exponent += !exponent;
|
|
|
|
yExponent += !yExponent;
|
|
|
|
exponent += yExponent;
|
|
|
|
exponent -= exponentBias;
|
|
|
|
++exponent;
|
|
|
|
if (exponent < 1) {
|
|
|
|
int rshift = 1 - exponent;
|
|
|
|
exponent = 1;
|
|
|
|
bool sticky{false};
|
|
|
|
if (rshift >= product.upper.bits + product.lower.bits) {
|
|
|
|
sticky = !product.lower.IsZero() || !product.upper.IsZero();
|
|
|
|
} else if (rshift >= product.lower.bits) {
|
|
|
|
sticky = !product.lower.IsZero();
|
|
|
|
} else {
|
|
|
|
sticky = !product.lower.IAND(product.lower.MASKR(rshift)).IsZero();
|
|
|
|
}
|
|
|
|
product.lower = product.lower.DSHIFTR(product.upper, rshift);
|
|
|
|
product.upper = product.upper.SHIFTR(rshift);
|
|
|
|
if (sticky) {
|
|
|
|
product.lower = product.lower.IBSET(0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
int leadz{product.upper.LEADZ()};
|
|
|
|
if (leadz >= product.upper.bits) {
|
|
|
|
leadz += product.lower.LEADZ();
|
|
|
|
}
|
|
|
|
int lshift{leadz};
|
|
|
|
if (lshift > exponent - 1) {
|
|
|
|
lshift = exponent - 1;
|
|
|
|
}
|
|
|
|
exponent -= lshift;
|
|
|
|
product.upper = product.upper.DSHIFTL(product.lower, lshift);
|
|
|
|
product.lower = product.lower.SHIFTL(lshift);
|
|
|
|
RoundingBits roundingBits{product.lower, product.upper.bits};
|
|
|
|
result.flags |= result.value.Normalize(
|
|
|
|
isNegative, exponent, product.upper, &roundingBits);
|
|
|
|
result.flags |= result.value.Round(rounding, roundingBits);
|
2018-06-05 22:55:56 +02:00
|
|
|
}
|
2018-06-05 00:56:40 +02:00
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2018-06-05 01:22:58 +02:00
|
|
|
constexpr ValueWithRealFlags<Real> Divide(
|
2018-06-08 19:24:15 +02:00
|
|
|
const Real &y, Rounding rounding = Rounding::TiesToEven) const {
|
2018-06-05 01:22:58 +02:00
|
|
|
ValueWithRealFlags<Real> result;
|
2018-06-05 22:55:56 +02:00
|
|
|
if (IsNotANumber() || y.IsNotANumber()) {
|
|
|
|
result.value.word_ = NaNWord(); // NaN / x -> NaN, x / NaN -> NaN
|
2018-06-06 20:11:35 +02:00
|
|
|
result.flags.set(RealFlag::InvalidArgument);
|
2018-06-05 22:55:56 +02:00
|
|
|
} else {
|
|
|
|
bool isNegative{IsNegative() != y.IsNegative()};
|
|
|
|
if (IsInfinite()) {
|
|
|
|
if (y.IsInfinite() || y.IsZero()) {
|
|
|
|
result.value.word_ = NaNWord(); // Inf/Inf -> NaN, Inf/0 -> Nan
|
2018-06-06 20:11:35 +02:00
|
|
|
result.flags.set(RealFlag::InvalidArgument);
|
2018-06-05 22:55:56 +02:00
|
|
|
} else {
|
|
|
|
result.value.Normalize(isNegative, maxExponent, Fraction{});
|
|
|
|
}
|
|
|
|
} else if (y.IsInfinite()) {
|
|
|
|
result.value.word_ = NaNWord(); // x/Inf -> NaN
|
2018-06-06 20:11:35 +02:00
|
|
|
result.flags.set(RealFlag::InvalidArgument);
|
2018-06-05 22:55:56 +02:00
|
|
|
} else {
|
|
|
|
auto qr = GetFraction().DivideUnsigned(y.GetFraction());
|
|
|
|
if (qr.divisionByZero) {
|
|
|
|
result.value.Normalize(isNegative, maxExponent, Fraction{});
|
2018-06-06 20:11:35 +02:00
|
|
|
result.flags.set(RealFlag::DivideByZero);
|
2018-06-05 22:55:56 +02:00
|
|
|
} else {
|
|
|
|
// To round, double the remainder and compare it to the divisor.
|
|
|
|
auto doubled = qr.remainder.AddUnsigned(qr.remainder);
|
|
|
|
Ordering drcmp{doubled.value.CompareUnsigned(y.GetFraction())};
|
2018-06-08 19:58:58 +02:00
|
|
|
RoundingBits roundingBits{
|
|
|
|
drcmp != Ordering::Less, drcmp != Ordering::Equal};
|
2018-06-05 22:55:56 +02:00
|
|
|
std::uint64_t exponent{Exponent() - y.Exponent() + exponentBias};
|
|
|
|
result.flags |=
|
|
|
|
result.value.Normalize(isNegative, exponent, qr.quotient);
|
|
|
|
result.flags |= result.value.Round(rounding, roundingBits);
|
|
|
|
}
|
|
|
|
}
|
2018-06-05 00:56:40 +02:00
|
|
|
}
|
|
|
|
return result;
|
2018-06-04 18:49:47 +02:00
|
|
|
}
|
|
|
|
|
2018-06-02 00:30:31 +02:00
|
|
|
private:
|
2018-06-05 22:55:56 +02:00
|
|
|
using Fraction = Integer<precision>; // all bits made explicit
|
|
|
|
using Significand = Integer<significandBits>; // no implicit bit
|
2018-06-04 18:49:47 +02:00
|
|
|
|
2018-06-08 19:58:58 +02:00
|
|
|
class RoundingBits {
|
|
|
|
public:
|
|
|
|
constexpr RoundingBits(
|
|
|
|
bool guard = false, bool round = false, bool sticky = false)
|
|
|
|
: guard_{guard}, round_{round}, sticky_{sticky} {}
|
|
|
|
|
|
|
|
template<typename FRACTION>
|
|
|
|
constexpr RoundingBits(const FRACTION &fraction, int rshift) {
|
|
|
|
if (rshift > 0 && rshift < fraction.bits + 1) {
|
|
|
|
guard_ = fraction.BTEST(rshift - 1);
|
|
|
|
}
|
|
|
|
if (rshift > 1 && rshift < fraction.bits + 2) {
|
|
|
|
round_ = fraction.BTEST(rshift - 2);
|
|
|
|
}
|
|
|
|
if (rshift > 2) {
|
|
|
|
if (rshift >= fraction.bits + 2) {
|
|
|
|
sticky_ = !fraction.IsZero();
|
|
|
|
} else {
|
|
|
|
auto mask = fraction.MASKR(rshift - 2);
|
|
|
|
sticky_ = !fraction.IAND(mask).IsZero();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
constexpr bool Zero() const { return !(guard_ | round_ | sticky_); }
|
|
|
|
|
|
|
|
constexpr bool Negate() {
|
|
|
|
bool carry{!sticky_};
|
|
|
|
if (carry) {
|
|
|
|
carry = !round_;
|
|
|
|
} else {
|
|
|
|
round_ = !round_;
|
|
|
|
}
|
|
|
|
if (carry) {
|
|
|
|
carry = !guard_;
|
|
|
|
} else {
|
|
|
|
guard_ = !guard_;
|
|
|
|
}
|
|
|
|
return carry;
|
|
|
|
}
|
|
|
|
|
|
|
|
constexpr bool ShiftLeft() {
|
|
|
|
bool oldGuard{guard_};
|
|
|
|
guard_ = round_;
|
|
|
|
round_ = sticky_;
|
|
|
|
return oldGuard;
|
|
|
|
}
|
|
|
|
|
|
|
|
constexpr void ShiftRight(bool newGuard) {
|
|
|
|
sticky_ |= round_;
|
|
|
|
round_ = guard_;
|
|
|
|
guard_ = newGuard;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Determines whether a value should be rounded by increasing its
|
|
|
|
// fraction, given a rounding mode and a summary of the lost bits.
|
|
|
|
constexpr bool MustRound(Rounding rounding, const Real &real) const {
|
|
|
|
bool round{false}; // to dodge bogus g++ warning about missing return
|
|
|
|
switch (rounding) {
|
|
|
|
case Rounding::TiesToEven:
|
|
|
|
round = guard_ && (round_ | sticky_ | real.RawBits().BTEST(0));
|
|
|
|
break;
|
|
|
|
case Rounding::ToZero: break;
|
|
|
|
case Rounding::Down: round = real.IsNegative() && !Zero(); break;
|
|
|
|
case Rounding::Up: round = !real.IsNegative() && !Zero(); break;
|
|
|
|
case Rounding::TiesAwayFromZero: round = guard_; break;
|
|
|
|
}
|
|
|
|
return round;
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
2018-06-08 19:24:15 +02:00
|
|
|
bool guard_{false};
|
|
|
|
bool round_{false};
|
|
|
|
bool sticky_{false};
|
2018-06-04 18:49:47 +02:00
|
|
|
};
|
2018-06-02 00:30:31 +02:00
|
|
|
|
2018-06-05 22:55:56 +02:00
|
|
|
constexpr Significand GetSignificand() const {
|
|
|
|
return Significand::Convert(word_).value;
|
|
|
|
}
|
|
|
|
|
|
|
|
constexpr Fraction GetFraction() const {
|
|
|
|
Fraction result{Fraction::Convert(word_).value};
|
|
|
|
if constexpr (!implicitMSB) {
|
|
|
|
return result;
|
2018-06-04 18:49:47 +02:00
|
|
|
} else {
|
2018-06-05 22:55:56 +02:00
|
|
|
std::uint64_t exponent{Exponent()};
|
|
|
|
if (exponent > 0 && exponent < maxExponent) {
|
|
|
|
return result.IBSET(significandBits);
|
|
|
|
} else {
|
|
|
|
return result.IBCLR(significandBits);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: Configurable NaN representations
|
|
|
|
static constexpr Word NaNWord() {
|
2018-06-08 19:24:15 +02:00
|
|
|
return Word{maxExponent}
|
|
|
|
.SHIFTL(significandBits)
|
|
|
|
.IBSET(significandBits - 1)
|
|
|
|
.IBSET(significandBits - 2);
|
2018-06-05 22:55:56 +02:00
|
|
|
}
|
|
|
|
|
2018-06-09 00:18:03 +02:00
|
|
|
constexpr RealFlags Normalize(bool negative, std::uint64_t exponent,
|
2018-06-08 19:24:15 +02:00
|
|
|
const Fraction &fraction, RoundingBits *roundingBits = nullptr) {
|
2018-06-09 00:18:03 +02:00
|
|
|
if (exponent >= maxExponent) {
|
|
|
|
word_ = Word{maxExponent}.SHIFTL(significandBits); // Inf
|
2018-06-06 01:29:26 +02:00
|
|
|
if (negative) {
|
|
|
|
word_ = word_.IBSET(bits - 1);
|
2018-06-05 22:55:56 +02:00
|
|
|
}
|
2018-06-06 20:11:35 +02:00
|
|
|
return {RealFlag::Overflow};
|
2018-06-09 00:18:03 +02:00
|
|
|
}
|
|
|
|
if (fraction.BTEST(fraction.bits - 1)) {
|
|
|
|
// fraction is normalized
|
|
|
|
word_ = Word::Convert(fraction).value;
|
|
|
|
if (exponent == 0) {
|
|
|
|
exponent = 1;
|
|
|
|
}
|
2018-06-05 22:55:56 +02:00
|
|
|
} else {
|
2018-06-08 19:24:15 +02:00
|
|
|
std::uint64_t lshift = fraction.LEADZ();
|
2018-06-09 00:18:03 +02:00
|
|
|
if (lshift >= fraction.bits) {
|
2018-06-05 22:55:56 +02:00
|
|
|
// +/-0.0
|
|
|
|
word_ = Word{};
|
2018-06-09 00:18:03 +02:00
|
|
|
exponent = 0;
|
2018-06-05 22:55:56 +02:00
|
|
|
} else {
|
2018-06-08 19:24:15 +02:00
|
|
|
word_ = Word::Convert(fraction).value;
|
2018-06-09 00:18:03 +02:00
|
|
|
if (lshift < exponent) {
|
|
|
|
exponent -= lshift;
|
|
|
|
} else if (exponent > 0) {
|
|
|
|
lshift = exponent - 1;
|
|
|
|
exponent = 0;
|
|
|
|
} else if (lshift == 0) {
|
|
|
|
exponent = 1;
|
2018-06-08 19:24:15 +02:00
|
|
|
} else {
|
|
|
|
lshift = 0;
|
|
|
|
}
|
|
|
|
if (lshift > 0) {
|
|
|
|
word_ = word_.SHIFTL(lshift);
|
|
|
|
if (roundingBits != nullptr) {
|
|
|
|
for (; lshift > 0; --lshift) {
|
2018-06-08 19:58:58 +02:00
|
|
|
if (roundingBits->ShiftLeft()) {
|
2018-06-08 19:24:15 +02:00
|
|
|
word_ = word_.IBSET(lshift - 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-06-04 18:49:47 +02:00
|
|
|
}
|
|
|
|
}
|
2018-06-09 00:18:03 +02:00
|
|
|
if (implicitMSB) {
|
|
|
|
word_ = word_.IBCLR(significandBits);
|
|
|
|
}
|
|
|
|
word_ = word_.IOR(Word{exponent}.SHIFTL(significandBits));
|
|
|
|
if (negative) {
|
|
|
|
word_ = word_.IBSET(bits - 1);
|
|
|
|
}
|
|
|
|
return {};
|
2018-06-04 18:49:47 +02:00
|
|
|
}
|
|
|
|
|
2018-06-06 20:11:35 +02:00
|
|
|
// Rounds a result, if necessary.
|
|
|
|
RealFlags Round(Rounding rounding, const RoundingBits &bits) {
|
2018-06-05 22:55:56 +02:00
|
|
|
std::uint64_t exponent{Exponent()};
|
2018-06-06 20:11:35 +02:00
|
|
|
RealFlags flags;
|
2018-06-08 19:58:58 +02:00
|
|
|
if (!bits.Zero()) {
|
2018-06-06 20:11:35 +02:00
|
|
|
flags.set(RealFlag::Inexact);
|
|
|
|
}
|
2018-06-08 19:58:58 +02:00
|
|
|
if (exponent < maxExponent && bits.MustRound(rounding, *this)) {
|
2018-06-05 22:55:56 +02:00
|
|
|
typename Fraction::ValueWithCarry sum{
|
|
|
|
GetFraction().AddUnsigned(Fraction{}, true)};
|
2018-06-04 18:49:47 +02:00
|
|
|
if (sum.carry) {
|
2018-06-08 19:24:15 +02:00
|
|
|
// The fraction was all ones before rounding; sum.value is now zero
|
2018-06-05 22:55:56 +02:00
|
|
|
if (++exponent < maxExponent) {
|
2018-06-08 19:24:15 +02:00
|
|
|
sum.value = sum.value.IBSET(precision - 1);
|
2018-06-05 22:55:56 +02:00
|
|
|
} else {
|
|
|
|
// rounded away to an infinity
|
2018-06-06 20:11:35 +02:00
|
|
|
flags.set(RealFlag::Overflow);
|
2018-06-05 22:55:56 +02:00
|
|
|
}
|
2018-06-02 00:30:31 +02:00
|
|
|
}
|
2018-06-05 22:55:56 +02:00
|
|
|
flags |= Normalize(IsNegative(), exponent, sum.value);
|
2018-06-02 00:30:31 +02:00
|
|
|
}
|
2018-06-05 22:55:56 +02:00
|
|
|
return flags;
|
2018-06-02 00:30:31 +02:00
|
|
|
}
|
|
|
|
|
2018-06-05 22:55:56 +02:00
|
|
|
Word word_{}; // an Integer<>
|
2018-06-02 00:30:31 +02:00
|
|
|
};
|
|
|
|
|
2018-06-06 01:55:31 +02:00
|
|
|
using RealKind2 = Real<Integer<16>, 11>;
|
2018-06-05 22:55:56 +02:00
|
|
|
extern template class Real<Integer<16>, 11>;
|
2018-06-06 01:55:31 +02:00
|
|
|
|
|
|
|
using RealKind4 = Real<Integer<32>, 24>;
|
2018-06-05 22:55:56 +02:00
|
|
|
extern template class Real<Integer<32>, 24>;
|
2018-06-06 01:55:31 +02:00
|
|
|
|
|
|
|
using RealKind8 = Real<Integer<64>, 53>;
|
2018-06-05 22:55:56 +02:00
|
|
|
extern template class Real<Integer<64>, 53>;
|
2018-06-06 01:55:31 +02:00
|
|
|
|
|
|
|
using RealKind10 = Real<Integer<80>, 64, false>; // 80387
|
2018-06-05 22:55:56 +02:00
|
|
|
extern template class Real<Integer<80>, 64, false>;
|
2018-06-06 01:55:31 +02:00
|
|
|
|
|
|
|
using RealKind16 = Real<Integer<128>, 112>;
|
2018-06-05 22:55:56 +02:00
|
|
|
extern template class Real<Integer<128>, 112>;
|
2018-06-05 00:56:40 +02:00
|
|
|
|
2018-06-06 01:55:31 +02:00
|
|
|
// N.B. No "double-double" support.
|
|
|
|
|
2018-06-02 00:30:31 +02:00
|
|
|
} // namespace Fortran::evaluate
|
|
|
|
#endif // FORTRAN_EVALUATE_REAL_H_
|