[flang] revamp Real::Read(), use it for mutual testing of ScaledDecimals

Original-commit: flang-compiler/f18@a2054bb9df
Reviewed-on: https://github.com/flang-compiler/f18/pull/225
Tree-same-pre-rewrite: false
This commit is contained in:
peter klausler 2018-11-13 16:02:52 -08:00
parent bf7c03dbfb
commit a10b7bc006
6 changed files with 177 additions and 105 deletions

View File

@ -22,16 +22,12 @@
namespace Fortran::evaluate {
template<typename REAL, typename INT>
ValueWithRealFlags<REAL> IntPower(
const REAL &base, const INT &power, Rounding rounding = defaultRounding) {
REAL one{REAL::FromInteger(INT{1}).value};
ValueWithRealFlags<REAL> result;
result.value = one;
ValueWithRealFlags<REAL> TimesIntPowerOf(const REAL &factor, const REAL &base,
const INT &power, Rounding rounding = defaultRounding) {
ValueWithRealFlags<REAL> result{factor};
if (base.IsNotANumber()) {
result.value = REAL::NotANumber();
if (base.IsSignalingNaN()) {
result.flags.set(RealFlag::InvalidArgument);
}
result.flags.set(RealFlag::InvalidArgument);
} else if (power.IsZero()) {
if (base.IsZero() || base.IsInfinite()) {
result.flags.set(RealFlag::InvalidArgument);
@ -39,21 +35,29 @@ ValueWithRealFlags<REAL> IntPower(
} else {
bool negativePower{power.IsNegative()};
INT absPower{power.ABS().value};
REAL shifted{base};
REAL squares{base};
int nbits{INT::bits - absPower.LEADZ()};
for (int j{0}; j + 1 < nbits; ++j) {
for (int j{0}; j < nbits; ++j) {
if (absPower.BTEST(j)) {
result.value =
result.value.Multiply(shifted).AccumulateFlags(result.flags);
if (negativePower) {
result.value =
result.value.Divide(squares).AccumulateFlags(result.flags);
} else {
result.value =
result.value.Multiply(squares).AccumulateFlags(result.flags);
}
}
shifted = shifted.Add(shifted).AccumulateFlags(result.flags);
}
result.value = result.value.Multiply(shifted).AccumulateFlags(result.flags);
if (negativePower) {
result.value = one.Divide(result.value).AccumulateFlags(result.flags);
squares = squares.Multiply(squares).AccumulateFlags(result.flags);
}
}
return result;
}
template<typename REAL, typename INT>
ValueWithRealFlags<REAL> IntPower(
const REAL &base, const INT &power, Rounding rounding = defaultRounding) {
REAL one{REAL::FromInteger(INT{1}).value};
return TimesIntPowerOf(one, base, power, rounding);
}
}
#endif // FORTRAN_EVALUATE_INT_POWER_H_

View File

@ -383,31 +383,58 @@ template<typename W, int P, bool IM>
ValueWithRealFlags<Real<W, P, IM>> Real<W, P, IM>::Read(
const char *&p, Rounding rounding) {
ValueWithRealFlags<Real> result;
Real ten{FromInteger(Integer<32>{10}).value};
for (; parser::IsDecimalDigit(*p); ++p) {
result.value =
result.value.Multiply(ten, rounding).AccumulateFlags(result.flags);
result.value =
result.value.Add(FromInteger(Integer<32>{*p - '0'}).value, rounding)
.AccumulateFlags(result.flags);
Real ten{FromInteger(Integer<8>{10}).value};
while (*p == ' ') {
++p;
}
std::int64_t exponent{0};
if (*p == '.') {
for (++p; parser::IsDecimalDigit(*p); ++p) {
--exponent;
result.value =
result.value.Multiply(ten, rounding).AccumulateFlags(result.flags);
result.value =
result.value.Add(FromInteger(Integer<32>{*p - '0'}).value, rounding)
.AccumulateFlags(result.flags);
bool isNegative{*p == '-'};
if (*p == '-' || *p == '+') {
++p;
}
Word integer{0};
int decimalExponent{0};
bool full{false};
bool inFraction{false};
for (;; ++p) {
if (*p == '.') {
if (inFraction) {
break;
}
inFraction = true;
} else if (!parser::IsDecimalDigit(*p)) {
break;
} else if (full) {
if (!inFraction) {
++decimalExponent;
}
} else {
auto times10{integer.MultiplyUnsigned(Word{10})};
if (!times10.upper.IsZero()) {
full = true;
if (!inFraction) {
++decimalExponent;
}
} else {
auto augmented{times10.lower.AddUnsigned(Word{*p - '0'})};
if (augmented.carry) {
full = true;
if (!inFraction) {
++decimalExponent;
}
} else {
integer = augmented.value;
if (inFraction) {
--decimalExponent;
}
}
}
}
}
if (parser::IsLetter(*p)) {
bool negExpo{false};
if (*++p == '-') {
negExpo = true;
++p;
} else if (*p == '+') {
++p;
bool negExpo{*p == '-'};
if (*p == '+' || *p == '-') {
++p;
}
auto expo{Integer<32>::ReadUnsigned(p)};
@ -417,20 +444,64 @@ ValueWithRealFlags<Real<W, P, IM>> Real<W, P, IM>::Read(
} else if (negExpo) {
expoVal *= -1;
}
exponent += expoVal;
decimalExponent += expoVal;
}
if (exponent == 0) {
return result;
}
Real tenPower{IntPower(ten, Integer<64>{std::abs(exponent)}, rounding)
.AccumulateFlags(result.flags)};
if (exponent > 0) {
result.value =
result.value.Multiply(tenPower, rounding).AccumulateFlags(result.flags);
int binaryExponent{exponentBias + bits - 1};
if (integer.IsZero()) {
decimalExponent = 0;
} else {
result.value =
result.value.Divide(tenPower, rounding).AccumulateFlags(result.flags);
int leadz{integer.LEADZ()};
binaryExponent -= leadz;
integer = integer.SHIFTL(leadz);
}
for (; decimalExponent > 0; --decimalExponent) {
auto times5{integer.MultiplyUnsigned(Word{5})};
++binaryExponent;
integer = times5.lower;
for (; !times5.upper.IsZero(); times5.upper = times5.upper.SHIFTR(1)) {
++binaryExponent;
integer = integer.SHIFTR(1);
if (times5.upper.BTEST(0)) {
integer = integer.IBSET(bits - 1);
}
}
}
for (; decimalExponent < 0; ++decimalExponent) {
auto div5{integer.DivideUnsigned(Word{5})};
--binaryExponent;
integer = div5.quotient;
std::uint8_t lost = div5.remainder.ToUInt64() * 0x33;
while (!integer.BTEST(bits - 1)) {
integer = integer.SHIFTL(1);
if (lost & 0x80) {
integer = integer.IBSET(0);
}
lost <<= 1;
--binaryExponent;
}
}
RoundingBits roundingBits;
for (int j{0}; bits - j > precision; ++j) {
roundingBits.ShiftRight(integer.BTEST(0));
integer = integer.SHIFTR(1);
}
Fraction fraction{Fraction::ConvertUnsigned(integer).value};
while (binaryExponent < 1) {
if (fraction.IsZero()) {
binaryExponent = 0;
break;
} else {
++binaryExponent;
roundingBits.ShiftRight(fraction.BTEST(0));
fraction = fraction.SHIFTR(1);
}
}
NormalizeAndRound(
result, isNegative, binaryExponent, fraction, rounding, roundingBits);
return result;
}
@ -580,30 +651,31 @@ auto Real<W, P, IM>::AsScaledDecimal(Rounding rounding) const
}
} else {
// Divide asInt by 2**(-twoPower).
unsigned lower3{0};
std::uint32_t lower{0};
for (; twoPower < 0; ++twoPower) {
auto times5{asInt.MultiplyUnsigned(five)};
if (!times5.upper.IsZero()) {
// asInt is too big to need scaling, just shift it down.
lower3 >>= 1;
lower >>= 1;
if (asInt.BTEST(0)) {
lower3 |= 4;
lower |= 1 << 31;
}
asInt = asInt.SHIFTR(1);
} else {
// asInt is small enough to be scaled; do so.
unsigned times5lower3{lower3 * 5};
unsigned round{times5lower3 >> 3};
std::uint64_t lowerTimes5{lower * static_cast<std::uint64_t>(5)};
std::uint32_t round = lowerTimes5 >> 32;
auto rounded{times5.lower.AddUnsigned(Word{round})};
if (rounded.carry) {
lower3 >>= 1;
// asInt is still too big to need scaling (rounding would overflow)
lower >>= 1;
if (asInt.BTEST(0)) {
lower3 |= 4;
lower |= 1 << 31;
}
asInt = asInt.SHIFTR(1);
} else {
// asInt is small enough to be scaled; do so.
--result.value.decimalExponent;
lower3 = times5lower3 & 7;
lower = lowerTimes5;
asInt = rounded.value;
}
}

View File

@ -91,27 +91,27 @@ CoarrayRef &CoarrayRef::set_team(Expr<SomeInteger> &&v, bool isTeamNumber) {
return *this;
}
void Substring::SetBounds(std::optional<Expr<SubscriptInteger>> &first,
std::optional<Expr<SubscriptInteger>> &last) {
if (first.has_value()) {
first_ = IndirectSubscriptIntegerExpr::Make(std::move(*first));
void Substring::SetBounds(std::optional<Expr<SubscriptInteger>> &lower,
std::optional<Expr<SubscriptInteger>> &upper) {
if (lower.has_value()) {
lower_ = IndirectSubscriptIntegerExpr::Make(std::move(*lower));
}
if (last.has_value()) {
last_ = IndirectSubscriptIntegerExpr::Make(std::move(*last));
if (upper.has_value()) {
upper_ = IndirectSubscriptIntegerExpr::Make(std::move(*upper));
}
}
Expr<SubscriptInteger> Substring::first() const {
if (first_.has_value()) {
return **first_;
Expr<SubscriptInteger> Substring::lower() const {
if (lower_.has_value()) {
return **lower_;
} else {
return AsExpr(Constant<SubscriptInteger>{1});
}
}
Expr<SubscriptInteger> Substring::last() const {
if (last_.has_value()) {
return **last_;
Expr<SubscriptInteger> Substring::upper() const {
if (upper_.has_value()) {
return **upper_;
} else {
return std::visit(
common::visitors{[](const DataRef &dataRef) { return dataRef.LEN(); },
@ -123,23 +123,23 @@ Expr<SubscriptInteger> Substring::last() const {
}
std::optional<Expr<SomeCharacter>> Substring::Fold(FoldingContext &context) {
if (!first_.has_value()) {
first_ = AsExpr(Constant<SubscriptInteger>{1});
if (!lower_.has_value()) {
lower_ = AsExpr(Constant<SubscriptInteger>{1});
}
*first_ = evaluate::Fold(context, std::move(**first_));
std::optional<std::int64_t> lbi{ToInt64(**first_)};
*lower_ = evaluate::Fold(context, std::move(**lower_));
std::optional<std::int64_t> lbi{ToInt64(**lower_)};
if (lbi.has_value() && *lbi < 1) {
context.messages.Say(
"lower bound (%jd) on substring is less than one"_en_US,
static_cast<std::intmax_t>(*lbi));
*lbi = 1;
first_ = AsExpr(Constant<SubscriptInteger>{1});
lower_ = AsExpr(Constant<SubscriptInteger>{1});
}
if (!last_.has_value()) {
last_ = last();
if (!upper_.has_value()) {
upper_ = upper();
}
*last_ = evaluate::Fold(context, std::move(**last_));
if (std::optional<std::int64_t> ubi{ToInt64(**last_)}) {
*upper_ = evaluate::Fold(context, std::move(**upper_));
if (std::optional<std::int64_t> ubi{ToInt64(**upper_)}) {
auto *literal{std::get_if<StaticDataObject::Pointer>(&parent_)};
std::optional<std::int64_t> length;
if (literal != nullptr) {
@ -150,8 +150,8 @@ std::optional<Expr<SomeCharacter>> Substring::Fold(FoldingContext &context) {
if (*ubi < 1 || (lbi.has_value() && *ubi < *lbi)) {
// Zero-length string: canonicalize
*lbi = 1, *ubi = 0;
first_ = AsExpr(Constant<SubscriptInteger>{*lbi});
last_ = AsExpr(Constant<SubscriptInteger>{*ubi});
lower_ = AsExpr(Constant<SubscriptInteger>{*lbi});
upper_ = AsExpr(Constant<SubscriptInteger>{*ubi});
} else if (length.has_value() && *ubi > *length) {
context.messages.Say("upper bound (&jd) on substring is greater "
"than character length (%jd)"_en_US,
@ -170,9 +170,9 @@ std::optional<Expr<SomeCharacter>> Substring::Fold(FoldingContext &context) {
newStaticData->data().push_back(from[j]);
}
parent_ = newStaticData;
first_ = AsExpr(Constant<SubscriptInteger>{1});
lower_ = AsExpr(Constant<SubscriptInteger>{1});
std::int64_t length = newStaticData->data().size();
last_ = AsExpr(Constant<SubscriptInteger>{length});
upper_ = AsExpr(Constant<SubscriptInteger>{length});
switch (width) {
case 1:
return {
@ -324,8 +324,8 @@ std::ostream &DataRef::AsFortran(std::ostream &o) const { return Emit(o, u); }
std::ostream &Substring::AsFortran(std::ostream &o) const {
Emit(o, parent_) << '(';
Emit(o, first_) << ':';
return Emit(o, last_);
Emit(o, lower_) << ':';
return Emit(o, upper_);
}
std::ostream &ComplexPart::AsFortran(std::ostream &o) const {
@ -380,7 +380,7 @@ Expr<SubscriptInteger> DataRef::LEN() const {
Expr<SubscriptInteger> Substring::LEN() const {
return AsExpr(
Extremum<SubscriptInteger>{AsExpr(Constant<SubscriptInteger>{0}),
last() - first() + AsExpr(Constant<SubscriptInteger>{1})});
upper() - lower() + AsExpr(Constant<SubscriptInteger>{1})});
}
template<typename T> Expr<SubscriptInteger> Designator<T>::LEN() const {
if constexpr (Result::category == TypeCategory::Character) {

View File

@ -193,14 +193,14 @@ public:
SetBounds(first, last);
}
Substring(StaticDataObject::Pointer &&parent,
std::optional<Expr<SubscriptInteger>> &&first,
std::optional<Expr<SubscriptInteger>> &&last)
std::optional<Expr<SubscriptInteger>> &&lower,
std::optional<Expr<SubscriptInteger>> &&upper)
: parent_{std::move(parent)} {
SetBounds(first, last);
SetBounds(lower, upper);
}
Expr<SubscriptInteger> first() const; // TODO pmk: lower/upper
Expr<SubscriptInteger> last() const;
Expr<SubscriptInteger> lower() const;
Expr<SubscriptInteger> upper() const;
int Rank() const;
BaseObject GetBaseObject() const;
const Symbol *GetLastSymbol() const;
@ -213,7 +213,7 @@ private:
void SetBounds(std::optional<Expr<SubscriptInteger>> &,
std::optional<Expr<SubscriptInteger>> &);
std::variant<DataRef, StaticDataObject::Pointer> parent_;
std::optional<IndirectSubscriptIntegerExpr> first_, last_;
std::optional<IndirectSubscriptIntegerExpr> lower_, upper_;
};
// R915 complex-part-designator

View File

@ -270,7 +270,9 @@ void ExprResolver::Resolve(Symbol &symbol) {
if (auto *type{symbol.GetType()}) {
if (type->category() == DeclTypeSpec::TypeDerived) {
DerivedTypeSpec &dts{type->derivedTypeSpec()};
for (auto &[name, value] : dts.paramValues()) {
for (auto &nameAndValue : dts.paramValues()) {
// &[name, value] elicits "unused variable" warnings
auto &value{nameAndValue.second};
if (value.isExplicit()) {
value.ResolveExplicit(context_);
}

View File

@ -367,9 +367,11 @@ void subsetTests(int pass, Rounding rounding, std::uint32_t opds) {
auto actualFlags{FlagsToBits(fpenv.CurrentFlags())};
actualFlags &= ~Inexact; // x86 std::trunc can set Inexact; AINT ain't
u.f = fcheck;
#ifndef __clang__
if (IsNaN(u.ui)) {
actualFlags |= InvalidArgument; // x86 std::trunc(NaN) WAR
actualFlags |= InvalidArgument; // x86 std::trunc(NaN) workaround
}
#endif
UINT rcheck{NormalizeNaN(u.ui)};
UINT check = aint.value.RawBits().ToUInt64();
MATCH(rcheck, check)
@ -384,10 +386,6 @@ void subsetTests(int pass, Rounding rounding, std::uint32_t opds) {
MATCH(IsInfinite(rj), x.IsInfinite())
("%d IsInfinite(0x%llx)", pass, static_cast<long long>(rj));
// Rounding mode doesn't affect the conversion of binary floating-point
// data to scaled decimal, but it does affect the check in which the
// result is converted back to binary floating-point, and can cause
// spurious failures.
if (rounding == Rounding::TiesToEven) {
auto scaled{x.AsScaledDecimal()};
if (IsNaN(rj)) {
@ -401,17 +399,13 @@ void subsetTests(int pass, Rounding rounding, std::uint32_t opds) {
MATCH(x.IsNegative(), scaled.value.negative)
("%d IsNegative(0x%llx)", pass, static_cast<long long>(rj));
char buffer[128];
const char *p = buffer;
snprintf(buffer, sizeof buffer, "%c%llu.0E%d",
"+-"[scaled.value.negative],
static_cast<unsigned long long>(integer),
scaled.value.decimalExponent);
if constexpr (std::is_same_v<FLT, float>) {
char *p;
u.f = std::strtof(buffer, &p);
} else {
u.f = std::atof(buffer);
}
MATCH(rj, u.ui)
auto readBack{REAL::Read(p, rounding)};
MATCH(rj, readBack.value.RawBits().ToUInt64())
("%d scaled decimal 0x%llx %s", pass, static_cast<long long>(rj),
buffer);
}