@@ -274,6 +274,7 @@ ValueWithRealFlags<Real<W, P>> Real<W, P>::SQRT(Rounding rounding) const {
274
274
// SQRT(-0) == -0 in IEEE-754.
275
275
result.value = NegativeZero ();
276
276
} else {
277
+ result.flags .set (RealFlag::InvalidArgument);
277
278
result.value = NotANumber ();
278
279
}
279
280
} else if (IsInfinite ()) {
@@ -297,53 +298,31 @@ ValueWithRealFlags<Real<W, P>> Real<W, P>::SQRT(Rounding rounding) const {
297
298
result.value .GetFraction ());
298
299
return result;
299
300
}
300
- // Compute the square root of the reduced value with the slow but
301
- // reliable bit-at-a-time method. Start with a clear significand and
302
- // half of the unbiased exponent, and then try to set significand bits
303
- // in descending order of magnitude without exceeding the exact result.
304
- expo = expo / 2 + exponentBias;
305
- result.value .Normalize (false , expo, Fraction::MASKL (1 ));
306
- Real initialSq{result.value .Multiply (result.value ).value };
307
- if (Compare (initialSq) == Relation::Less) {
308
- // Initial estimate is too large; this can happen for values just
309
- // under 1.0.
310
- --expo;
311
- result.value .Normalize (false , expo, Fraction::MASKL (1 ));
312
- }
313
- for (int bit{significandBits - 1 }; bit >= 0 ; --bit) {
314
- Word word{result.value .word_ };
315
- result.value .word_ = word.IBSET (bit);
316
- auto squared{result.value .Multiply (result.value , rounding)};
317
- if (squared.flags .test (RealFlag::Overflow) ||
318
- squared.flags .test (RealFlag::Underflow) ||
319
- Compare (squared.value ) == Relation::Less) {
320
- result.value .word_ = word;
321
- }
322
- }
323
- // The computed square root has a square that's not greater than the
324
- // original argument. Check this square against the square of the next
325
- // larger Real and return that one if its square is closer in magnitude to
326
- // the original argument.
327
- Real resultSq{result.value .Multiply (result.value ).value };
328
- Real diff{Subtract (resultSq).value .ABS ()};
329
- if (diff.IsZero ()) {
330
- return result; // exact
331
- }
332
- Real ulp;
333
- ulp.Normalize (false , expo, Fraction::MASKR (1 ));
334
- Real nextAfter{result.value .Add (ulp).value };
335
- auto nextAfterSq{nextAfter.Multiply (nextAfter)};
336
- if (!nextAfterSq.flags .test (RealFlag::Overflow) &&
337
- !nextAfterSq.flags .test (RealFlag::Underflow)) {
338
- Real nextAfterDiff{Subtract (nextAfterSq.value ).value .ABS ()};
339
- if (nextAfterDiff.Compare (diff) == Relation::Less) {
340
- result.value = nextAfter;
341
- if (nextAfterDiff.IsZero ()) {
342
- return result; // exact
343
- }
301
+ // (-1) <= expo <= 1; use it as a shift to set the desired square.
302
+ using Extended = typename value::Integer<(binaryPrecision + 2 )>;
303
+ Extended goal{
304
+ Extended::ConvertUnsigned (GetFraction ()).value .SHIFTL (expo + 1 )};
305
+ // Calculate the exact square root by maximizing a value whose square
306
+ // does not exceed the goal. Use two extra bits of precision for
307
+ // rounding.
308
+ bool sticky{true };
309
+ Extended extFrac{};
310
+ for (int bit{Extended::bits - 1 }; bit >= 0 ; --bit) {
311
+ Extended next{extFrac.IBSET (bit)};
312
+ auto squared{next.MultiplyUnsigned (next)};
313
+ auto cmp{squared.upper .CompareUnsigned (goal)};
314
+ if (cmp == Ordering::Less) {
315
+ extFrac = next;
316
+ } else if (cmp == Ordering::Equal && squared.lower .IsZero ()) {
317
+ extFrac = next;
318
+ sticky = false ;
319
+ break ; // exact result
344
320
}
345
321
}
346
- result.flags .set (RealFlag::Inexact);
322
+ RoundingBits roundingBits{extFrac.BTEST (1 ), extFrac.BTEST (0 ), sticky};
323
+ NormalizeAndRound (result, false , exponentBias,
324
+ Fraction::ConvertUnsigned (extFrac.SHIFTR (2 )).value , rounding,
325
+ roundingBits);
347
326
}
348
327
return result;
349
328
}
0 commit comments