toStringAsPrecisionFast method

String toStringAsPrecisionFast(
  1. int requiredPrecision
)

Provides a faster approach than the orginal method

Implementation

String toStringAsPrecisionFast(int requiredPrecision) {
  assert(requiredPrecision > 0);

  // this method is not able to manage rationals with infinite precision,
  // so we transform all numbers with infinite precision into
  // finite precision ones.
  // It is not important to calculate the exact precision of the new
  // number, what is fundament is that the new precision must not be lesser
  // than the required one.
  // The exact calculation will be done in the recursive call to
  // this method.
  if (!hasFinitePrecision) {
    /// in case of fractional digits starting with many zeros,
    /// we risk to loose precision, so, to compensate,
    /// we add the denominator precision.
    /// As there is not a precision method in the BigInt class
    /// we use the shortcut of creating a string and calculating
    /// the length. Maybe that there is a cleaner way,
    var pwr = requiredPrecision + denominator.toRadixString(10).length;
    var shifter = _r10.pow(pwr);
    var rational = (this * shifter).round() / shifter;
    return rational.toStringAsPrecisionFast(requiredPrecision);
  }

  /// The shift exponent is used to calculate the value of the number
  /// to round in order to loose precision (if exceeding the required one)
  ///
  /// `(precision - scale)`
  /// here we calculate how many digits and in which direction we have to
  /// move the fractional separator in order to obtain fields of the format
  /// `0.xxxxxxx`
  /// For example:
  ///              1230   125.78   0.0034
  ///              ^ (4)  ^ (3)    (-2)^
  ///             (4-0=>4)(5-2=>3)(2-4=>-2)
  ///              .1230  .12578   .34
  /// Now that we have the field in `0.xxxxx` format, we shift left for the
  /// number of required precision digits.
  /// For example:
  /// Precision -> 3
  ///              .1230  .12578   .34
  ///               123   125.78   340
  /// Precision -> 1
  ///              .1230  .12578   .34
  ///              1.230  1.2578   3.4
  /// As the required precision augments the exponent that we calculate,
  /// the previous calculation (precision - scale), that goes in the opposite
  /// direction, must be subtracted.
  /// The exponent for shifting the value is made by the formula:
  /// `requiredPrecision - (precision - scale)`
  var shiftExponent = requiredPrecision - (precision - scale);

  Rational value;
  if (shiftExponent == 0) {
    /// No shifting needed
    value = this;
  } else {
    /// given the exponent, we calculate the value to be used in order
    /// to shift our number
    var coefficient = _r10.power(shiftExponent);

    /// here we shift the number and round, so that we loose the non required
    /// precision digits (if any), and then we move back the digits in the
    /// opposite direction.
    value = (this * coefficient).round() / coefficient;
  }

  return shiftExponent <= 0
      ? value.toString()
      : value.toStringAsFixed(shiftExponent);
}