squareRoot method

FpvType squareRoot(
  1. FloatingPointValue value, {
  2. FloatingPointRoundingMode roundingMode = FloatingPointRoundingMode.roundNearestEven,
})

Computes square root exactly and rounds once into this format.

Implementation

FpvType squareRoot(FloatingPointValue value,
    {FloatingPointRoundingMode roundingMode =
        FloatingPointRoundingMode.roundNearestEven}) {
  if (value.isNaN) {
    return _quietNaNFrom(value);
  }
  if (value.isAZero) {
    return _zero(value.sign.toBool());
  }
  if (value.sign.toBool()) {
    return nan;
  }
  if (value.isAnInfinity) {
    return _infinityOrLargestFinite(
        negative: false, roundingMode: roundingMode);
  }

  final exact = _decodeFinite(value);
  final floorLog2 = exact.significand.bitLength - 1 + exact.exponent;
  var resultExponent =
      floorLog2 >= 0 ? floorLog2 ~/ 2 : -((-floorLog2 + 1) ~/ 2);
  final isSubnormal = resultExponent < minExponent;
  if (isSubnormal) {
    resultExponent = minExponent;
  }

  final fractionWidth = mantissaWidth - (explicitJBit ? 1 : 0);
  final scaleExponent = exact.exponent + 2 * (fractionWidth - resultExponent);
  final BigInt truncated;
  final bool exactResult;
  final int midpointComparison;
  if (scaleExponent >= 0) {
    final scaledRadicand = exact.significand << scaleExponent;
    truncated = _integerSquareRoot(scaledRadicand);
    exactResult = truncated * truncated == scaledRadicand;
    final midpoint = (truncated << 1) + BigInt.one;
    midpointComparison = (scaledRadicand << 2).compareTo(midpoint * midpoint);
  } else {
    final denominatorShift = -scaleExponent;
    truncated = _integerSquareRoot(exact.significand >> denominatorShift);
    exactResult =
        (truncated * truncated) << denominatorShift == exact.significand;
    final midpoint = (truncated << 1) + BigInt.one;
    midpointComparison = (exact.significand << 2)
        .compareTo((midpoint * midpoint) << denominatorShift);
  }

  final increment = !exactResult &&
      switch (roundingMode) {
        FloatingPointRoundingMode.truncate ||
        FloatingPointRoundingMode.roundTowardsZero ||
        FloatingPointRoundingMode.roundTowardsNegativeInfinity =>
          false,
        FloatingPointRoundingMode.roundTowardsInfinity => true,
        FloatingPointRoundingMode.roundNearestTiesAway =>
          midpointComparison >= 0,
        FloatingPointRoundingMode.roundNearestEven =>
          midpointComparison > 0 ||
              (midpointComparison == 0 && truncated.isOdd),
      };
  final significand = increment ? truncated + BigInt.one : truncated;

  return _packRounded(significand, resultExponent,
      negative: false, wasSubnormal: isSubnormal, roundingMode: roundingMode);
}