[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:
parent
bf7c03dbfb
commit
a10b7bc006
|
@ -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_
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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_);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue