Skip to content

Commit

Permalink
[APFloat] Implement PPCDoubleDouble add and subtract.
Browse files Browse the repository at this point in the history
Summary:
I looked at libgcc's implementation (which is based on the paper,
Software for Doubled-Precision Floating-Point Computations", by Seppo Linnainmaa,
ACM TOMS vol 7 no 3, September 1981, pages 272-283.) and made it generic to
arbitrary IEEE floats.

Differential Revision: https://reviews.llvm.org/D26817

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@289472 91177308-0d34-0410-b5e6-96231b3b80d8
  • Loading branch information
timshen91 committed Dec 12, 2016
1 parent 953d731 commit 611d8de
Show file tree
Hide file tree
Showing 3 changed files with 393 additions and 23 deletions.
71 changes: 67 additions & 4 deletions include/llvm/ADT/APFloat.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ struct fltSemantics;
class APSInt;
class StringRef;
class APFloat;
class raw_ostream;

template <typename T> class SmallVectorImpl;

Expand Down Expand Up @@ -479,6 +480,8 @@ class IEEEFloat final : public APFloatBase {

/// @}

cmpResult compareAbsoluteValue(const IEEEFloat &) const;

private:
/// \name Simple Queries
/// @{
Expand Down Expand Up @@ -527,7 +530,6 @@ class IEEEFloat final : public APFloatBase {
bool convertFromStringSpecials(StringRef str);
opStatus normalize(roundingMode, lostFraction);
opStatus addOrSubtract(const IEEEFloat &, roundingMode, bool subtract);
cmpResult compareAbsoluteValue(const IEEEFloat &) const;
opStatus handleOverflow(roundingMode);
bool roundAwayFromZero(roundingMode, lostFraction, unsigned int) const;
opStatus convertToSignExtendedInteger(integerPart *, unsigned int, bool,
Expand Down Expand Up @@ -600,6 +602,12 @@ class DoubleAPFloat final : public APFloatBase {
const fltSemantics *Semantics;
std::unique_ptr<APFloat[]> Floats;

opStatus addImpl(const APFloat &a, const APFloat &aa, const APFloat &c,
const APFloat &cc, roundingMode RM);

opStatus addWithSpecial(const DoubleAPFloat &LHS, const DoubleAPFloat &RHS,
DoubleAPFloat &Out, roundingMode RM);

public:
DoubleAPFloat(const fltSemantics &S);
DoubleAPFloat(const fltSemantics &S, uninitializedTag);
Expand All @@ -623,6 +631,19 @@ class DoubleAPFloat final : public APFloatBase {

APFloat &getFirst() { return Floats[0]; }
const APFloat &getFirst() const { return Floats[0]; }
APFloat &getSecond() { return Floats[1]; }
const APFloat &getSecond() const { return Floats[1]; }

opStatus add(const DoubleAPFloat &RHS, roundingMode RM);
opStatus subtract(const DoubleAPFloat &RHS, roundingMode RM);
void changeSign();
cmpResult compareAbsoluteValue(const DoubleAPFloat &RHS) const;

fltCategory getCategory() const;
bool isNegative() const;

void makeInf(bool Neg);
void makeNaN(bool SNaN, bool Neg, const APInt *fill);
};

} // End detail namespace
Expand Down Expand Up @@ -747,7 +768,15 @@ class APFloat : public APFloatBase {

void makeZero(bool Neg) { getIEEE().makeZero(Neg); }

void makeInf(bool Neg) { getIEEE().makeInf(Neg); }
void makeInf(bool Neg) {
if (usesLayout<IEEEFloat>(*U.semantics)) {
return U.IEEE.makeInf(Neg);
} else if (usesLayout<DoubleAPFloat>(*U.semantics)) {
return U.Double.makeInf(Neg);
} else {
llvm_unreachable("Unexpected semantics");
}
}

void makeNaN(bool SNaN, bool Neg, const APInt *fill) {
getIEEE().makeNaN(SNaN, Neg, fill);
Expand All @@ -772,6 +801,17 @@ class APFloat : public APFloatBase {
explicit APFloat(DoubleAPFloat F, const fltSemantics &S)
: U(std::move(F), S) {}

cmpResult compareAbsoluteValue(const APFloat &RHS) const {
assert(&getSemantics() == &RHS.getSemantics());
if (usesLayout<IEEEFloat>(getSemantics())) {
return U.IEEE.compareAbsoluteValue(RHS.U.IEEE);
} else if (usesLayout<DoubleAPFloat>(getSemantics())) {
return U.Double.compareAbsoluteValue(RHS.U.Double);
} else {
llvm_unreachable("Unexpected semantics");
}
}

public:
APFloat(const fltSemantics &Semantics) : U(Semantics) {}
APFloat(const fltSemantics &Semantics, StringRef S);
Expand Down Expand Up @@ -885,10 +925,22 @@ class APFloat : public APFloatBase {
void Profile(FoldingSetNodeID &NID) const { getIEEE().Profile(NID); }

opStatus add(const APFloat &RHS, roundingMode RM) {
return getIEEE().add(RHS.getIEEE(), RM);
if (usesLayout<IEEEFloat>(getSemantics())) {
return U.IEEE.add(RHS.U.IEEE, RM);
} else if (usesLayout<DoubleAPFloat>(getSemantics())) {
return U.Double.add(RHS.U.Double, RM);
} else {
llvm_unreachable("Unexpected semantics");
}
}
opStatus subtract(const APFloat &RHS, roundingMode RM) {
return getIEEE().subtract(RHS.getIEEE(), RM);
if (usesLayout<IEEEFloat>(getSemantics())) {
return U.IEEE.subtract(RHS.U.IEEE, RM);
} else if (usesLayout<DoubleAPFloat>(getSemantics())) {
return U.Double.subtract(RHS.U.Double, RM);
} else {
llvm_unreachable("Unexpected semantics");
}
}
opStatus multiply(const APFloat &RHS, roundingMode RM) {
return getIEEE().multiply(RHS.getIEEE(), RM);
Expand Down Expand Up @@ -1011,14 +1063,25 @@ class APFloat : public APFloatBase {
return getIEEE().toString(Str, FormatPrecision, FormatMaxPadding);
}

void print(raw_ostream &) const;
void dump() const;

bool getExactInverse(APFloat *inv) const {
return getIEEE().getExactInverse(inv ? &inv->getIEEE() : nullptr);
}

// This is for internal test only.
// TODO: Remove it after the PPCDoubleDouble transition.
const APFloat &getSecondFloat() const {
assert(&getSemantics() == &PPCDoubleDouble);
return U.Double.getSecond();
}

friend hash_code hash_value(const APFloat &Arg);
friend int ilogb(const APFloat &Arg) { return ilogb(Arg.getIEEE()); }
friend APFloat scalbn(APFloat X, int Exp, roundingMode RM);
friend APFloat frexp(const APFloat &X, int &Exp, roundingMode RM);
friend IEEEFloat;
friend DoubleAPFloat;
};

Expand Down
210 changes: 207 additions & 3 deletions lib/Support/APFloat.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,10 @@
#include "llvm/ADT/Hashing.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/MathExtras.h"
#include "llvm/Support/raw_ostream.h"
#include <cstring>
#include <limits.h>

Expand Down Expand Up @@ -3847,8 +3849,9 @@ DoubleAPFloat::DoubleAPFloat(const fltSemantics &S, integerPart I)
}

DoubleAPFloat::DoubleAPFloat(const fltSemantics &S, const APInt &I)
: Semantics(&S), Floats(new APFloat[2]{APFloat(PPCDoubleDoubleImpl, I),
APFloat(IEEEdouble)}) {
: Semantics(&S), Floats(new APFloat[2]{
APFloat(PPCDoubleDoubleImpl, I),
APFloat(IEEEdouble, APInt(64, I.getRawData()[1]))}) {
assert(Semantics == &PPCDoubleDouble);
}

Expand All @@ -3858,7 +3861,8 @@ DoubleAPFloat::DoubleAPFloat(const fltSemantics &S, APFloat &&First,
Floats(new APFloat[2]{std::move(First), std::move(Second)}) {
assert(Semantics == &PPCDoubleDouble);
// TODO Check for First == &IEEEdouble once the transition is done.
assert(&Floats[0].getSemantics() == &PPCDoubleDoubleImpl);
assert(&Floats[0].getSemantics() == &PPCDoubleDoubleImpl ||
&Floats[0].getSemantics() == &IEEEdouble);
assert(&Floats[1].getSemantics() == &IEEEdouble);
}

Expand Down Expand Up @@ -3887,6 +3891,198 @@ DoubleAPFloat &DoubleAPFloat::operator=(const DoubleAPFloat &RHS) {
return *this;
}

// "Software for Doubled-Precision Floating-Point Computations",
// by Seppo Linnainmaa, ACM TOMS vol 7 no 3, September 1981, pages 272-283.
APFloat::opStatus DoubleAPFloat::addImpl(const APFloat &a, const APFloat &aa,
const APFloat &c, const APFloat &cc,
roundingMode RM) {
int Status = opOK;
APFloat z = a;
Status |= z.add(c, RM);
if (!z.isFinite()) {
if (!z.isInfinity()) {
Floats[0] = std::move(z);
Floats[1].makeZero(false);
return (opStatus)Status;
}
Status = opOK;
auto AComparedToC = a.compareAbsoluteValue(c);
z = cc;
Status |= z.add(aa, RM);
if (AComparedToC == APFloat::cmpGreaterThan) {
// z = cc + aa + c + a;
Status |= z.add(c, RM);
Status |= z.add(a, RM);
} else {
// z = cc + aa + a + c;
Status |= z.add(a, RM);
Status |= z.add(c, RM);
}
if (!z.isFinite()) {
Floats[0] = std::move(z);
Floats[1].makeZero(false);
return (opStatus)Status;
}
Floats[0] = z;
APFloat zz = aa;
Status |= zz.add(cc, RM);
if (AComparedToC == APFloat::cmpGreaterThan) {
// Floats[1] = a - z + c + zz;
Floats[1] = a;
Status |= Floats[1].subtract(z, RM);
Status |= Floats[1].add(c, RM);
Status |= Floats[1].add(zz, RM);
} else {
// Floats[1] = c - z + a + zz;
Floats[1] = c;
Status |= Floats[1].subtract(z, RM);
Status |= Floats[1].add(a, RM);
Status |= Floats[1].add(zz, RM);
}
} else {
// q = a - z;
APFloat q = a;
Status |= q.subtract(z, RM);

// zz = q + c + (a - (q + z)) + aa + cc;
// Compute a - (q + z) as -((q + z) - a) to avoid temporary copies.
auto zz = q;
Status |= zz.add(c, RM);
Status |= q.add(z, RM);
Status |= q.subtract(a, RM);
q.changeSign();
Status |= zz.add(q, RM);
Status |= zz.add(aa, RM);
Status |= zz.add(cc, RM);
if (zz.isZero() && !zz.isNegative()) {
Floats[0] = std::move(z);
Floats[1].makeZero(false);
return opOK;
}
Floats[0] = z;
Status |= Floats[0].add(zz, RM);
if (!Floats[0].isFinite()) {
Floats[1].makeZero(false);
return (opStatus)Status;
}
Floats[1] = std::move(z);
Status |= Floats[1].subtract(Floats[0], RM);
Status |= Floats[1].add(zz, RM);
}
return (opStatus)Status;
}

APFloat::opStatus DoubleAPFloat::addWithSpecial(const DoubleAPFloat &LHS,
const DoubleAPFloat &RHS,
DoubleAPFloat &Out,
roundingMode RM) {
if (LHS.getCategory() == fcNaN) {
Out = LHS;
return opOK;
}
if (RHS.getCategory() == fcNaN) {
Out = RHS;
return opOK;
}
if (LHS.getCategory() == fcZero) {
Out = RHS;
return opOK;
}
if (RHS.getCategory() == fcZero) {
Out = LHS;
return opOK;
}
if (LHS.getCategory() == fcInfinity && RHS.getCategory() == fcInfinity &&
LHS.isNegative() != RHS.isNegative()) {
Out.makeNaN(false, Out.isNegative(), nullptr);
return opInvalidOp;
}
if (LHS.getCategory() == fcInfinity) {
Out = LHS;
return opOK;
}
if (RHS.getCategory() == fcInfinity) {
Out = RHS;
return opOK;
}
assert(LHS.getCategory() == fcNormal && RHS.getCategory() == fcNormal);

// These conversions will go away once PPCDoubleDoubleImpl goes away.
// (PPCDoubleDoubleImpl, IEEEDouble) -> (IEEEDouble, IEEEDouble)
APFloat A(IEEEdouble,
APInt(64, LHS.Floats[0].bitcastToAPInt().getRawData()[0])),
AA(LHS.Floats[1]),
C(IEEEdouble, APInt(64, RHS.Floats[0].bitcastToAPInt().getRawData()[0])),
CC(RHS.Floats[1]);
assert(&AA.getSemantics() == &IEEEdouble);
assert(&CC.getSemantics() == &IEEEdouble);
Out.Floats[0] = APFloat(IEEEdouble);
assert(&Out.Floats[1].getSemantics() == &IEEEdouble);

auto Ret = Out.addImpl(A, AA, C, CC, RM);

// (IEEEDouble, IEEEDouble) -> (PPCDoubleDoubleImpl, IEEEDouble)
uint64_t Buffer[] = {Out.Floats[0].bitcastToAPInt().getRawData()[0],
Out.Floats[1].bitcastToAPInt().getRawData()[0]};
Out.Floats[0] = APFloat(PPCDoubleDoubleImpl, APInt(128, 2, Buffer));
return Ret;
}

APFloat::opStatus DoubleAPFloat::add(const DoubleAPFloat &RHS,
roundingMode RM) {
return addWithSpecial(*this, RHS, *this, RM);
}

APFloat::opStatus DoubleAPFloat::subtract(const DoubleAPFloat &RHS,
roundingMode RM) {
changeSign();
auto Ret = add(RHS, RM);
changeSign();
return Ret;
}

void DoubleAPFloat::changeSign() {
Floats[0].changeSign();
Floats[1].changeSign();
}

APFloat::cmpResult
DoubleAPFloat::compareAbsoluteValue(const DoubleAPFloat &RHS) const {
auto Result = Floats[0].compareAbsoluteValue(RHS.Floats[0]);
if (Result != cmpEqual)
return Result;
Result = Floats[1].compareAbsoluteValue(RHS.Floats[1]);
if (Result == cmpLessThan || Result == cmpGreaterThan) {
auto Against = Floats[0].isNegative() ^ Floats[1].isNegative();
auto RHSAgainst = RHS.Floats[0].isNegative() ^ RHS.Floats[1].isNegative();
if (Against && !RHSAgainst)
return cmpLessThan;
if (!Against && RHSAgainst)
return cmpGreaterThan;
if (!Against && !RHSAgainst)
return Result;
if (Against && RHSAgainst)
return (cmpResult)(cmpLessThan + cmpGreaterThan - Result);
}
return Result;
}

APFloat::fltCategory DoubleAPFloat::getCategory() const {
return Floats[0].getCategory();
}

bool DoubleAPFloat::isNegative() const { return Floats[0].isNegative(); }

void DoubleAPFloat::makeInf(bool Neg) {
Floats[0].makeInf(Neg);
Floats[1].makeZero(false);
}

void DoubleAPFloat::makeNaN(bool SNaN, bool Neg, const APInt *fill) {
Floats[0].makeNaN(SNaN, Neg, fill);
Floats[1].makeZero(false);
}

} // End detail namespace

APFloat::Storage::Storage(IEEEFloat F, const fltSemantics &Semantics) {
Expand Down Expand Up @@ -3959,4 +4155,12 @@ APFloat APFloat::getAllOnesValue(unsigned BitWidth, bool isIEEE) {
}
}

void APFloat::print(raw_ostream &OS) const {
SmallVector<char, 16> Buffer;
toString(Buffer);
OS << Buffer << "\n";
}

void APFloat::dump() const { print(dbgs()); }

} // End llvm namespace
Loading

0 comments on commit 611d8de

Please sign in to comment.