@@ -4949,6 +4949,21 @@ DoubleAPFloat &DoubleAPFloat::operator=(const DoubleAPFloat &RHS) {
4949
4949
return *this ;
4950
4950
}
4951
4951
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
+
4952
4967
// Implement addition, subtraction, multiplication and division based on:
4953
4968
// "Software for Doubled-Precision Floating-Point Computations",
4954
4969
// by Seppo Linnainmaa, ACM TOMS vol 7 no 3, September 1981, pages 272-283.
@@ -5218,10 +5233,78 @@ DoubleAPFloat::fusedMultiplyAdd(const DoubleAPFloat &Multiplicand,
5218
5233
5219
5234
APFloat::opStatus DoubleAPFloat::roundToIntegral (APFloat::roundingMode RM) {
5220
5235
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;
5225
5308
}
5226
5309
5227
5310
void DoubleAPFloat::changeSign () {
0 commit comments