Skip to content

Commit 0a23b22

Browse files
committed
[APFloat] Properly implement DoubleAPFloat::roundToIntegral
The previous implementation did not correctly handle double-doubles like 0x1p100 + 0x1p1 as the low order component would need more than a 106-bit significand to represent.
1 parent 51e825d commit 0a23b22

File tree

2 files changed

+595
-65
lines changed

2 files changed

+595
-65
lines changed

llvm/lib/Support/APFloat.cpp

Lines changed: 87 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4949,6 +4949,21 @@ DoubleAPFloat &DoubleAPFloat::operator=(const DoubleAPFloat &RHS) {
49494949
return *this;
49504950
}
49514951

4952+
// Returns a result such that:
4953+
// 1. abs(Lo) <= ulp(Hi)/2
4954+
// 2. Hi == RTNE(Hi + Lo)
4955+
// 3. Hi + Lo == X + Y
4956+
//
4957+
// Requires that log2(X) >= log2(Y).
4958+
static std::pair<APFloat, APFloat> fastTwoSum(APFloat X, APFloat Y) {
4959+
if (!X.isFinite())
4960+
return {X, APFloat::getZero(X.getSemantics(), /*Negative=*/false)};
4961+
APFloat Hi = X + Y;
4962+
APFloat Delta = Hi - X;
4963+
APFloat Lo = Y - Delta;
4964+
return {Hi, Lo};
4965+
}
4966+
49524967
// Implement addition, subtraction, multiplication and division based on:
49534968
// "Software for Doubled-Precision Floating-Point Computations",
49544969
// by Seppo Linnainmaa, ACM TOMS vol 7 no 3, September 1981, pages 272-283.
@@ -5218,10 +5233,78 @@ DoubleAPFloat::fusedMultiplyAdd(const DoubleAPFloat &Multiplicand,
52185233

52195234
APFloat::opStatus DoubleAPFloat::roundToIntegral(APFloat::roundingMode RM) {
52205235
assert(Semantics == &semPPCDoubleDouble && "Unexpected Semantics");
5221-
APFloat Tmp(semPPCDoubleDoubleLegacy, bitcastToAPInt());
5222-
auto Ret = Tmp.roundToIntegral(RM);
5223-
*this = DoubleAPFloat(semPPCDoubleDouble, Tmp.bitcastToAPInt());
5224-
return Ret;
5236+
const APFloat &Hi = getFirst();
5237+
const APFloat &Lo = getSecond();
5238+
5239+
APFloat RoundedHi = Hi;
5240+
const opStatus HiStatus = RoundedHi.roundToIntegral(RM);
5241+
5242+
// We can reduce the problem to just the high part if the input:
5243+
// 1. Represents a non-finite value.
5244+
// 2. Has a component which is zero.
5245+
if (!Hi.isFiniteNonZero() || Lo.isZero()) {
5246+
Floats[0] = std::move(RoundedHi);
5247+
Floats[1].makeZero(/*Neg=*/false);
5248+
return HiStatus;
5249+
}
5250+
5251+
// Adjust `Rounded` in the direction of `TieBreaker` if `ToRound` was at a
5252+
// halfway point.
5253+
auto RoundToNearestHelper = [](APFloat ToRound, APFloat Rounded,
5254+
APFloat TieBreaker) {
5255+
// RoundingError tells us which direction we rounded:
5256+
// - RoundingError > 0: we rounded up.
5257+
// - RoundingError < 0: we rounded down.
5258+
// Sterbenz' lemma ensures that RoundingError is exact.
5259+
const APFloat RoundingError = Rounded - ToRound;
5260+
if (TieBreaker.isNonZero() &&
5261+
TieBreaker.isNegative() != RoundingError.isNegative() &&
5262+
abs(RoundingError).isExactlyValue(0.5))
5263+
Rounded.add(
5264+
APFloat::getOne(Rounded.getSemantics(), TieBreaker.isNegative()),
5265+
rmNearestTiesToEven);
5266+
return Rounded;
5267+
};
5268+
5269+
// Case 1: Hi is not an integer.
5270+
// Special cases are for rounding modes that are sensitive to ties.
5271+
if (RoundedHi != Hi) {
5272+
// We need to consider the case where Hi was between two integers and the
5273+
// rounding mode broke the tie when, in fact, Lo may have had a different
5274+
// sign than Hi.
5275+
if (RM == rmNearestTiesToAway || RM == rmNearestTiesToEven)
5276+
RoundedHi = RoundToNearestHelper(Hi, RoundedHi, Lo);
5277+
5278+
Floats[0] = std::move(RoundedHi);
5279+
Floats[1].makeZero(/*Neg=*/false);
5280+
return HiStatus;
5281+
}
5282+
5283+
// Case 2: Hi is an integer.
5284+
// Special cases are for rounding modes which are rounding towards or away from zero.
5285+
RoundingMode LoRoundingMode;
5286+
if (RM == rmTowardZero)
5287+
// When our input is positive, we want the Lo component rounded toward
5288+
// negative infinity to get the smallest result magnitude. Likewise,
5289+
// negative inputs want the Lo component rounded toward positive infinity.
5290+
LoRoundingMode = isNegative() ? rmTowardPositive : rmTowardNegative;
5291+
else
5292+
LoRoundingMode = RM;
5293+
5294+
APFloat RoundedLo = Lo;
5295+
const opStatus LoStatus = RoundedLo.roundToIntegral(LoRoundingMode);
5296+
if (LoRoundingMode == rmNearestTiesToAway)
5297+
// We need to consider the case where Lo was between two integers and the
5298+
// rounding mode broke the tie when, in fact, Hi may have had a different
5299+
// sign than Lo.
5300+
RoundedLo = RoundToNearestHelper(Lo, RoundedLo, Hi);
5301+
5302+
// We must ensure that the final result has no overlap between the two APFloat values.
5303+
std::tie(RoundedHi, RoundedLo) = fastTwoSum(RoundedHi, RoundedLo);
5304+
5305+
Floats[0] = std::move(RoundedHi);
5306+
Floats[1] = std::move(RoundedLo);
5307+
return LoStatus;
52255308
}
52265309

52275310
void DoubleAPFloat::changeSign() {

0 commit comments

Comments
 (0)