Skip to content

Commit 9fdb5e1

Browse files
committed
[APFloat] Properly implement next for DoubleAPFloat
Rather than converting to the legacy 106-bit format, perform next() on the low APFloat. Of course, we need to renormalize the two APFloats if either of the two constraints are violated: 1. abs(low) <= ulp(high)/2 2. high = rtne(high + low) Should renormalization be needed, it will increment the high component and set low to the smallest value which obeys these rules.
1 parent b63a9b7 commit 9fdb5e1

File tree

2 files changed

+407
-4
lines changed

2 files changed

+407
-4
lines changed

llvm/lib/Support/APFloat.cpp

Lines changed: 126 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -900,6 +900,30 @@ writeSignedDecimal (char *dst, int value)
900900
return dst;
901901
}
902902

903+
// Compute the ULP of the input using a definition from:
904+
// Jean-Michel Muller. On the definition of ulp(x). [Research Report] RR-5504,
905+
// LIP RR-2005-09, INRIA, LIP. 2005, pp.16. inria-00070503
906+
static APFloat harrisonUlp(const APFloat &X) {
907+
const fltSemantics &Sem = X.getSemantics();
908+
switch (X.getCategory()) {
909+
case APFloat::fcNaN:
910+
return APFloat::getQNaN(Sem);
911+
case APFloat::fcInfinity:
912+
return APFloat::getInf(Sem);
913+
case APFloat::fcZero:
914+
return APFloat::getSmallest(Sem);
915+
case APFloat::fcNormal:
916+
break;
917+
}
918+
if (X.isDenormal() || X.isSmallestNormalized())
919+
return APFloat::getSmallest(Sem);
920+
int Exp = ilogb(X);
921+
if (X.getExactLog2() != INT_MIN)
922+
Exp -= 1;
923+
return scalbn(APFloat::getOne(Sem), Exp - (Sem.precision - 1),
924+
APFloat::rmNearestTiesToEven);
925+
}
926+
903927
namespace detail {
904928
/* Constructors. */
905929
void IEEEFloat::initialize(const fltSemantics *ourSemantics) {
@@ -5306,12 +5330,110 @@ Expected<APFloat::opStatus> DoubleAPFloat::convertFromString(StringRef S,
53065330
return Ret;
53075331
}
53085332

5333+
// The double-double lattice of values corresponds to numbers which obey:
5334+
// - abs(lo) <= 1/2 * ulp(hi)
5335+
// - roundTiesToEven(hi + lo) == hi
5336+
//
5337+
// nextUp must choose the smallest output > input that follows these rules.
5338+
// nexDown must choose the largest output < input that follows these rules.
53095339
APFloat::opStatus DoubleAPFloat::next(bool nextDown) {
53105340
assert(Semantics == &semPPCDoubleDouble && "Unexpected Semantics");
5311-
APFloat Tmp(semPPCDoubleDoubleLegacy, bitcastToAPInt());
5312-
auto Ret = Tmp.next(nextDown);
5313-
*this = DoubleAPFloat(semPPCDoubleDouble, Tmp.bitcastToAPInt());
5314-
return Ret;
5341+
// nextDown(x) = -nextUp(-x)
5342+
if (nextDown) {
5343+
changeSign();
5344+
APFloat::opStatus Result = next(/*nextDown=*/false);
5345+
changeSign();
5346+
return Result;
5347+
}
5348+
switch (getCategory()) {
5349+
case fcInfinity:
5350+
// nextUp(+inf) = +inf
5351+
// nextUp(-inf) = -getLargest()
5352+
if (isNegative())
5353+
makeLargest(true);
5354+
return opOK;
5355+
5356+
case fcNaN:
5357+
// IEEE-754R 2008 6.2 Par 2: nextUp(sNaN) = qNaN. Set Invalid flag.
5358+
// IEEE-754R 2008 6.2: nextUp(qNaN) = qNaN. Must be identity so we do not
5359+
// change the payload.
5360+
if (getFirst().isSignaling()) {
5361+
// For consistency, propagate the sign of the sNaN to the qNaN.
5362+
makeNaN(false, isNegative(), nullptr);
5363+
return opInvalidOp;
5364+
}
5365+
return opOK;
5366+
5367+
case fcZero:
5368+
// nextUp(pm 0) = +getSmallest()
5369+
makeSmallest(false);
5370+
return opOK;
5371+
5372+
case fcNormal:
5373+
break;
5374+
}
5375+
5376+
const APFloat &HiOld = getFirst();
5377+
const APFloat &LoOld = getSecond();
5378+
5379+
APFloat NextLo = LoOld;
5380+
NextLo.next(/*nextDown=*/false);
5381+
5382+
// We want to admit values where:
5383+
// 1. abs(Lo) <= ulp(Hi)/2
5384+
// 2. Hi == RTNE(Hi + lo)
5385+
auto InLattice = [](const APFloat &Hi, const APFloat &Lo) {
5386+
return Hi + Lo == Hi;
5387+
};
5388+
5389+
// Check if (HiOld, nextUp(LoOld) is in the lattice.
5390+
if (InLattice(HiOld, NextLo)) {
5391+
// Yes, the result is (HiOld, nextUp(LoOld)).
5392+
Floats[1] = std::move(NextLo);
5393+
5394+
// TODO: Because we currently rely on semPPCDoubleDoubleLegacy, our maximum
5395+
// value is defined to have exactly 106 bits of precision. This limitation
5396+
// results in semPPCDoubleDouble being unable to reach its maximum canonical
5397+
// value.
5398+
DoubleAPFloat Largest{*Semantics, uninitialized};
5399+
Largest.makeLargest(/*Neg=*/false);
5400+
if (compare(Largest) == cmpGreaterThan)
5401+
makeInf(/*Neg=*/false);
5402+
5403+
return opOK;
5404+
}
5405+
5406+
// Now we need to handle the cases where (HiOld, nextUp(LoOld)) is not the
5407+
// correct result. We know the new hi component will be nextUp(HiOld) but our
5408+
// lattice rules make it a little ambiguous what the correct NextLo must be.
5409+
APFloat NextHi = HiOld;
5410+
NextHi.next(/*nextDown=*/false);
5411+
5412+
// nextUp(getLargest()) == INFINITY
5413+
if (NextHi.isInfinity()) {
5414+
makeInf(/*Neg=*/false);
5415+
return opOK;
5416+
}
5417+
5418+
// IEEE 754-2019 5.3.1:
5419+
// "If x is the negative number of least magnitude in x's format, nextUp(x) is
5420+
// -0."
5421+
if (NextHi.isZero()) {
5422+
makeZero(/*Neg=*/true);
5423+
return opOK;
5424+
}
5425+
5426+
// abs(NextLo) must be <= ulp(NextHi)/2. We want NextLo to be as close to
5427+
// negative infinity as possible.
5428+
NextLo = neg(scalbn(harrisonUlp(NextHi), -1, rmTowardZero));
5429+
if (!InLattice(NextHi, NextLo))
5430+
// RTNE may mean that Lo must be < ulp(NextHi) / 2 so we bump NextLo.
5431+
NextLo.next(/*nextDown=*/false);
5432+
5433+
Floats[0] = std::move(NextHi);
5434+
Floats[1] = std::move(NextLo);
5435+
5436+
return opOK;
53155437
}
53165438

53175439
APFloat::opStatus

0 commit comments

Comments
 (0)