aboutsummaryrefslogtreecommitdiff
path: root/llvm/lib/Support/APFloat.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'llvm/lib/Support/APFloat.cpp')
-rw-r--r--llvm/lib/Support/APFloat.cpp130
1 files changed, 126 insertions, 4 deletions
diff --git a/llvm/lib/Support/APFloat.cpp b/llvm/lib/Support/APFloat.cpp
index 5e0b29f..46084c5 100644
--- a/llvm/lib/Support/APFloat.cpp
+++ b/llvm/lib/Support/APFloat.cpp
@@ -900,6 +900,30 @@ writeSignedDecimal (char *dst, int value)
return dst;
}
+// Compute the ULP of the input using a definition from:
+// Jean-Michel Muller. On the definition of ulp(x). [Research Report] RR-5504,
+// LIP RR-2005-09, INRIA, LIP. 2005, pp.16. inria-00070503
+static APFloat harrisonUlp(const APFloat &X) {
+ const fltSemantics &Sem = X.getSemantics();
+ switch (X.getCategory()) {
+ case APFloat::fcNaN:
+ return APFloat::getQNaN(Sem);
+ case APFloat::fcInfinity:
+ return APFloat::getInf(Sem);
+ case APFloat::fcZero:
+ return APFloat::getSmallest(Sem);
+ case APFloat::fcNormal:
+ break;
+ }
+ if (X.isDenormal() || X.isSmallestNormalized())
+ return APFloat::getSmallest(Sem);
+ int Exp = ilogb(X);
+ if (X.getExactLog2() != INT_MIN)
+ Exp -= 1;
+ return scalbn(APFloat::getOne(Sem), Exp - (Sem.precision - 1),
+ APFloat::rmNearestTiesToEven);
+}
+
namespace detail {
/* Constructors. */
void IEEEFloat::initialize(const fltSemantics *ourSemantics) {
@@ -5306,12 +5330,110 @@ Expected<APFloat::opStatus> DoubleAPFloat::convertFromString(StringRef S,
return Ret;
}
+// The double-double lattice of values corresponds to numbers which obey:
+// - abs(lo) <= 1/2 * ulp(hi)
+// - roundTiesToEven(hi + lo) == hi
+//
+// nextUp must choose the smallest output > input that follows these rules.
+// nexDown must choose the largest output < input that follows these rules.
APFloat::opStatus DoubleAPFloat::next(bool nextDown) {
assert(Semantics == &semPPCDoubleDouble && "Unexpected Semantics");
- APFloat Tmp(semPPCDoubleDoubleLegacy, bitcastToAPInt());
- auto Ret = Tmp.next(nextDown);
- *this = DoubleAPFloat(semPPCDoubleDouble, Tmp.bitcastToAPInt());
- return Ret;
+ // nextDown(x) = -nextUp(-x)
+ if (nextDown) {
+ changeSign();
+ APFloat::opStatus Result = next(/*nextDown=*/false);
+ changeSign();
+ return Result;
+ }
+ switch (getCategory()) {
+ case fcInfinity:
+ // nextUp(+inf) = +inf
+ // nextUp(-inf) = -getLargest()
+ if (isNegative())
+ makeLargest(true);
+ return opOK;
+
+ case fcNaN:
+ // IEEE-754R 2008 6.2 Par 2: nextUp(sNaN) = qNaN. Set Invalid flag.
+ // IEEE-754R 2008 6.2: nextUp(qNaN) = qNaN. Must be identity so we do not
+ // change the payload.
+ if (getFirst().isSignaling()) {
+ // For consistency, propagate the sign of the sNaN to the qNaN.
+ makeNaN(false, isNegative(), nullptr);
+ return opInvalidOp;
+ }
+ return opOK;
+
+ case fcZero:
+ // nextUp(pm 0) = +getSmallest()
+ makeSmallest(false);
+ return opOK;
+
+ case fcNormal:
+ break;
+ }
+
+ const APFloat &HiOld = getFirst();
+ const APFloat &LoOld = getSecond();
+
+ APFloat NextLo = LoOld;
+ NextLo.next(/*nextDown=*/false);
+
+ // We want to admit values where:
+ // 1. abs(Lo) <= ulp(Hi)/2
+ // 2. Hi == RTNE(Hi + lo)
+ auto InLattice = [](const APFloat &Hi, const APFloat &Lo) {
+ return Hi + Lo == Hi;
+ };
+
+ // Check if (HiOld, nextUp(LoOld) is in the lattice.
+ if (InLattice(HiOld, NextLo)) {
+ // Yes, the result is (HiOld, nextUp(LoOld)).
+ Floats[1] = std::move(NextLo);
+
+ // TODO: Because we currently rely on semPPCDoubleDoubleLegacy, our maximum
+ // value is defined to have exactly 106 bits of precision. This limitation
+ // results in semPPCDoubleDouble being unable to reach its maximum canonical
+ // value.
+ DoubleAPFloat Largest{*Semantics, uninitialized};
+ Largest.makeLargest(/*Neg=*/false);
+ if (compare(Largest) == cmpGreaterThan)
+ makeInf(/*Neg=*/false);
+
+ return opOK;
+ }
+
+ // Now we need to handle the cases where (HiOld, nextUp(LoOld)) is not the
+ // correct result. We know the new hi component will be nextUp(HiOld) but our
+ // lattice rules make it a little ambiguous what the correct NextLo must be.
+ APFloat NextHi = HiOld;
+ NextHi.next(/*nextDown=*/false);
+
+ // nextUp(getLargest()) == INFINITY
+ if (NextHi.isInfinity()) {
+ makeInf(/*Neg=*/false);
+ return opOK;
+ }
+
+ // IEEE 754-2019 5.3.1:
+ // "If x is the negative number of least magnitude in x's format, nextUp(x) is
+ // -0."
+ if (NextHi.isZero()) {
+ makeZero(/*Neg=*/true);
+ return opOK;
+ }
+
+ // abs(NextLo) must be <= ulp(NextHi)/2. We want NextLo to be as close to
+ // negative infinity as possible.
+ NextLo = neg(scalbn(harrisonUlp(NextHi), -1, rmTowardZero));
+ if (!InLattice(NextHi, NextLo))
+ // RTNE may mean that Lo must be < ulp(NextHi) / 2 so we bump NextLo.
+ NextLo.next(/*nextDown=*/false);
+
+ Floats[0] = std::move(NextHi);
+ Floats[1] = std::move(NextLo);
+
+ return opOK;
}
APFloat::opStatus