/// <summary> /// Converts a single precision floating point number to a string. /// </summary> /// <param name="result">The location where the result will be placed.</param> /// <param name="value">The value to format.</param> /// <param name="precision">The precision to output when formatting.</param> /// <param name="options">The format mode to use.</param> /// <param name="provider">The provider used to determine how the value will be /// formatted.</param> public static void ToString(StringBuilder result, float value, int precision, RyuFormatOptions options = RyuFormatOptions.RoundtripMode, IFormatProvider provider = null) { var info = RyuUtils.GetFormatInfo(provider); // Step 1: Decode the floating-point number, and unify normalized and subnormal // cases bool ieeeSign = RyuFloat32.Decode(value, out uint ieeeMantissa, out uint ieeeExponent); // Case distinction; exit early for the easy cases bool mZero = ieeeMantissa == 0UL; if (ieeeExponent == RyuFloat32.EXPONENT_MASK) { RyuUtils.GenerateSpecial(result, ieeeSign, !mZero, info); } else if (mZero && ieeeExponent == 0UL) { RyuUtils.GenerateZero(result, ieeeSign, precision, options, info); } else if ((options & RyuFormatOptions.RoundtripMode) != 0) { new RyuFloat32(ieeeSign, ieeeMantissa, ieeeExponent).ToRoundtripString(result, options, info); } }
private static bool HasTrailingZeroes(int exponent, int rexp, ulong mantissa) { int requiredTwos = -exponent - rexp; return(requiredTwos <= 0 || (requiredTwos < 60 && RyuUtils.IsMultipleOf2Power( mantissa, requiredTwos))); }
/// <summary> /// Decode a double into its sign, mantissa, and exponent. /// </summary> internal static bool Decode(double value, out ulong mantissa, out uint exponent) { ulong bits = RyuUtils.double_to_bits(value); mantissa = bits & MANTISSA_MASK; exponent = (uint)(bits >> DOUBLE_MANTISSA_BITS) & EXPONENT_MASK; return((bits & SIGN_MASK) != 0U); }
/// <summary> /// Decode a float into its sign, mantissa, and exponent. /// </summary> internal static bool Decode(float value, out uint mantissa, out uint exponent) { uint bits = RyuUtils.float_to_bits(value); mantissa = bits & MANTISSA_MASK; exponent = (bits >> FLOAT_MANTISSA_BITS) & EXPONENT_MASK; return((bits & SIGN_MASK) != 0U); }
/// <summary> /// Convert a 32-bit Ryu floating point number to a roundtrip notation string. /// </summary> public void ToRoundtripString(StringBuilder result, RyuFormatOptions options, NumberFormatInfo info = null) { // Step 5: Print the decimal representation if (info == null) { info = CultureInfo.CurrentCulture.NumberFormat; } if (sign) { result.Append(info.NegativeSign); } #if DEBUG if (info.NumberDecimalSeparator.Length > 1) { throw new ArgumentException("Requires a single character decimal point"); } #endif // Print the decimal digits uint mantissa = this.mantissa; int olength = RyuUtils.DecimalLength9(mantissa), start = result.Length, index = olength + start; result.Length = index + 1; index = RyuUtils.PrintGroups42(result, ref mantissa, index); // Group of 1 if (mantissa >= 10U) { string digits = RyuTables.DIGIT_TABLE[mantissa]; // We can't use memcpy here: the decimal dot goes between these two digits result[index--] = digits[1]; result[start] = digits[0]; } else { result[start] = mantissa.DigitToChar(); } // Print decimal point if needed if (olength > 1) { result[start + 1] = info.NumberDecimalSeparator[0]; index += olength; } result.Length = index; RyuUtils.AppendExponent(result, exponent + olength - 1, options, info); }
public RyuFloat64(bool sign, ulong ieeeMantissa, uint ieeeExponent) { // Subtract 2 more so that the bounds computation has 2 additional bits const int DOUBLE_EXP_DIFF_P2 = DOUBLE_BIAS + DOUBLE_MANTISSA_BITS + 2; int exponent; ulong mantissa; if (ieeeExponent == 0) { exponent = 1 - DOUBLE_EXP_DIFF_P2; mantissa = ieeeMantissa; } else { exponent = (int)ieeeExponent - DOUBLE_EXP_DIFF_P2; mantissa = (1UL << DOUBLE_MANTISSA_BITS) | ieeeMantissa; } // Step 2: Determine the interval of valid decimal representations bool even = (mantissa & 1UL) == 0UL, acceptBounds = even, mmShift = ieeeMantissa != 0U || ieeeExponent <= 1U; ulong mv = mantissa << 2; // Step 3: Convert to a decimal power base using 128-bit arithmetic ulong vr, vm, vp; int e10; bool vmTrailingZeroes = false, vrTrailingZeroes = false; if (exponent >= 0) { // Tried special-casing q == 0, but there was no effect on performance // This expression is slightly faster than max(0, log10Pow2(e2) - 1) int q = RyuUtils.Log10Pow2(exponent); if (exponent > 3) { q--; } int k = DOUBLE_POW5_INV_BITCOUNT + RyuUtils.Pow5Bits(q) - 1, i = -exponent + q + k; ulong a = RyuTables.double_computeInvPow5(q, out ulong b); e10 = q; vr = RyuUtils.MulShiftAll(mantissa, a, b, i, out vp, out vm, mmShift); if (q <= 21U) { // This should use q <= 22, but I think 21 is also safe. Smaller values // may still be safe, but it's more difficult to reason about them. // Only one of mp, mv, and mm can be a multiple of 5, if any uint mvMod5 = (uint)mv % 5U; if (mvMod5 == 0U) { vrTrailingZeroes = RyuUtils.IsMultipleOf5Power(mv, q); } else if (acceptBounds) { // Same as min(e2 + (~mm & 1), pow5Factor(mm)) >= q // <=> e2 + (~mm & 1) >= q && pow5Factor(mm) >= q // <=> true && pow5Factor(mm) >= q, since e2 >= q vmTrailingZeroes = RyuUtils.IsMultipleOf5Power(mv - 1UL - (mmShift ? 1UL : 0UL), q); } else { // Same as min(e2 + 1, pow5Factor(mp)) >= q vp -= RyuUtils.IsMultipleOf5Power(mv + 2UL, q) ? 1UL : 0UL; } } } else { // This expression is slightly faster than max(0, log10Pow5(-e2) - 1) int q = RyuUtils.Log10Pow5(-exponent); if (-exponent > 1) { q--; } int i = -exponent - q, k = RyuUtils.Pow5Bits(i) - DOUBLE_POW5_BITCOUNT, j = q - k; ulong a = RyuTables.double_computePow5(i, out ulong b); e10 = q + exponent; vr = RyuUtils.MulShiftAll(mantissa, a, b, j, out vp, out vm, mmShift); if (q <= 1U) { // {vr,vp,vm} is trailing zeros if {mv,mp,mm} has at least q trailing 0 // bits; mv = 4 * m2, so it always has at least two trailing 0 bits vrTrailingZeroes = true; if (acceptBounds) { // mm = mv - 1 - mmShift, so it has 1 trailing 0 bit iff mmShift == 1 vmTrailingZeroes = mmShift; } else { // mp = mv + 2, so it always has at least one trailing 0 bit --vp; } } else if (q < 63U) { // We want to know if the fUL product has at least q trailing zeros // We need to compute min(p2(mv), p5(mv) - e2) >= q // <=> p2(mv) >= q && p5(mv) - e2 >= q // <=> p2(mv) >= q (because -e2 >= q) vrTrailingZeroes = RyuUtils.IsMultipleOf2Power(mv, q); } } // Step 4: Find the shortest decimal representation in the interval of valid // representations int removed = 0; uint removedDigit = 0U; ulong output, p10, m10; // On average, we remove ~2 digits if (vmTrailingZeroes || vrTrailingZeroes) { // General case, which happens rarely (~0.7%) while ((p10 = vp / 10UL) > (m10 = vm.DivMod(10U, out uint vmRem))) { if (vmRem != 0U) { vmTrailingZeroes = false; } if (removedDigit != 0U) { vrTrailingZeroes = false; } vr = vr.DivMod(10U, out removedDigit); vp = p10; vm = m10; removed++; } if (vmTrailingZeroes) { while (((uint)vm - 10U * (uint)(m10 = vm / 10UL)) == 0U) { if (removedDigit != 0U) { vrTrailingZeroes = false; } vr = vr.DivMod(10U, out removedDigit); vp /= 10UL; vm = m10; removed++; } } if (vrTrailingZeroes && removedDigit == 5U && vr % 2U == 0U) { // Round even if the exact number is .....50..0 removedDigit = 4U; } // We need to take vr + 1 if vr is outside bounds or we need to round up output = vr; if ((vr == vm && (!acceptBounds || !vmTrailingZeroes)) || removedDigit >= 5U) { output++; } } else { // Specialized for the common case (~99.3%); percentages below are relative to // this bool roundUp = false; if (RyuUtils.DivCompare(ref vp, ref vm, 100UL)) { // Optimization: remove two digits at a time (~86.2%) vr = vr.DivMod(100U, out uint round100); roundUp = round100 >= 50U; removed += 2; } // Loop iterations below (approximately), without optimization above: // 0: 0.03%, 1: 13.8%, 2: 70.6%, 3: 14.0%, 4: 1.40%, 5: 0.14%, 6+: 0.02% // Loop iterations below (approximately), with optimization above: // 0: 70.6%, 1: 27.8%, 2: 1.40%, 3: 0.14%, 4+: 0.02% while (RyuUtils.DivCompare(ref vp, ref vm, 10UL)) { vr = vr.DivMod(10U, out uint vrRem); roundUp = vrRem >= 5U; removed++; } // We need to take vr + 1 if vr is outside bounds or we need to round up output = vr; if (vr == vm || roundUp) { output++; } } this.sign = sign; this.exponent = e10 + removed; this.mantissa = output; }
/// <summary> /// Convert a 64-bit Ryu floating point number to a roundtrip notation string. /// </summary> public void ToRoundtripString(StringBuilder result, RyuFormatOptions options, NumberFormatInfo info = null) { // Step 5: Print the decimal representation if (info == null) { info = CultureInfo.CurrentCulture.NumberFormat; } if (sign) { result.Append(info.NegativeSign); } #if DEBUG if (info.NumberDecimalSeparator.Length > 1) { throw new ArgumentException("Requires a single character decimal point"); } #endif ulong mantissa = this.mantissa; uint mantissaShort; int olength = RyuUtils.DecimalLength17(mantissa), start = result.Length, index = olength + start; result.Length = index + 1; // Print the decimal digits: group of 8 if ((mantissa >> 32) != 0U) { // We prefer 32-bit operations, even on 64-bit platforms. // We have at most 17 digits, and uint can store 9 digits. // If output doesn't fit into uint, we cut off 8 digits, // so the rest will fit into uint ulong q = mantissa / 100000000UL; mantissaShort = (uint)mantissa - 100000000U * (uint)q; uint o10000 = mantissaShort / 10000U; uint c0 = mantissaShort - 10000U * o10000, d0 = o10000 % 10000U; uint c1 = c0 / 100U, d1 = d0 / 100U; mantissa = q; index = result.WriteDigits(index, c0 - 100U * c1); index = result.WriteDigits(index, c1); index = result.WriteDigits(index, d0 - 100U * d1); index = result.WriteDigits(index, d1); } mantissaShort = (uint)mantissa; index = RyuUtils.PrintGroups42(result, ref mantissaShort, index); // Group of 1 if (mantissaShort >= 10U) { string digits = RyuTables.DIGIT_TABLE[mantissaShort]; // We can't use memcpy here: the decimal dot goes between these two digits result[index--] = digits[1]; result[start] = digits[0]; } else { result[start] = mantissaShort.DigitToChar(); } // Print decimal point if needed if (olength > 1) { result[start + 1] = info.NumberDecimalSeparator[0]; index += olength; } result.Length = index; RyuUtils.AppendExponent(result, exponent + olength - 1, options, info); }
/// <summary> /// Creates a Ryu float from an IEEE 32 bit float. /// </summary> public RyuFloat32(bool fSign, uint ieeeMantissa, uint ieeeExponent) { const int FLOAT_EXP_DIFF_P2 = FLOAT_BIAS + FLOAT_MANTISSA_BITS + 2; int exponent; uint mantissa; if (ieeeExponent == 0U) { // Subtract 2 so that the bounds computation has 2 additional bits exponent = 1 - FLOAT_EXP_DIFF_P2; mantissa = ieeeMantissa; } else { exponent = (int)ieeeExponent - FLOAT_EXP_DIFF_P2; mantissa = (1U << FLOAT_MANTISSA_BITS) | ieeeMantissa; } bool even = (mantissa & 1U) == 0U, acceptBounds = even, mmShift = ieeeMantissa != 0 || ieeeExponent <= 1; // Step 2: Determine the interval of valid decimal representations uint mv = mantissa << 2, mp = mv + 2U, mm = mv - 1U - (mmShift ? 1U : 0U); // Step 3: Convert to a decimal power base using 64-bit arithmetic uint vr, vp, vm; int e10; bool vmTrailingZeroes = false, vrTrailingZeroes = false; uint removedDigit = 0U; if (exponent >= 0) { int q = RyuUtils.Log10Pow2(exponent), k = FLOAT_POW5_INV_BITCOUNT + RyuUtils. Pow5Bits(q) - 1, i = -exponent + q + k; e10 = q; vr = RyuTables.MulPow5InvDivPow2(mv, q, i); vp = RyuTables.MulPow5InvDivPow2(mp, q, i); vm = RyuTables.MulPow5InvDivPow2(mm, q, i); if (q != 0U && (vp - 1U) / 10U <= vm / 10U) { // We need to know one removed digit even if we are not going to loop // below. We could use q = X - 1 above, except that would require 33 bits // for the result, and we've found that 32-bit arithmetic is faster even on // 64-bit machines int l = FLOAT_POW5_INV_BITCOUNT + RyuUtils.Pow5Bits(q - 1) - 1; removedDigit = RyuTables.MulPow5InvDivPow2(mv, q - 1, -exponent + q - 1 + l) % 10U; } if (q <= 9U) { // The largest power of 5 that fits in 24 bits is 5^10, but q <= 9 seems to // be safe as well. Only one of mp, mv, and mm can be a multiple of 5, if // any if (mv % 5U == 0U) { vrTrailingZeroes = RyuUtils.IsMultipleOf5Power(mv, q); } else if (acceptBounds) { vmTrailingZeroes = RyuUtils.IsMultipleOf5Power(mm, q); } else if (RyuUtils.IsMultipleOf5Power(mp, q)) { vp -= 1U; } } } else { int q = RyuUtils.Log10Pow5(-exponent), i = -exponent - q, k = RyuUtils. Pow5Bits(i) - FLOAT_POW5_BITCOUNT, j = q - k; e10 = q + exponent; vr = RyuTables.MulPow5DivPow2(mv, i, j); vp = RyuTables.MulPow5DivPow2(mp, i, j); vm = RyuTables.MulPow5DivPow2(mm, i, j); if (q != 0U && (vp - 1U) / 10U <= vm / 10U) { j = q - 1 - (RyuUtils.Pow5Bits(i + 1) - FLOAT_POW5_BITCOUNT); removedDigit = RyuTables.MulPow5DivPow2(mv, i + 1, j) % 10U; } if (q <= 1U) { // {vr,vp,vm} is trailing zeros if {mv,mp,mm} has at least q trailing 0 // bits. mv = 4 * m2, so it always has at least two trailing 0 bits vrTrailingZeroes = true; if (acceptBounds) { // mm = mv - 1 - mmShift, so it has 1 trailing 0 bit iff mmShift == 1 vmTrailingZeroes = mmShift; } else { // mp = mv + 2, so it always has at least one trailing 0 bit --vp; } } else if (q < 31U) { vrTrailingZeroes = RyuUtils.IsMultipleOf2Power(mv, q - 1); } } // Step 4: Find the shortest decimal representation in the interval of valid // representations int removed = 0; uint output, m10, p10; if (vmTrailingZeroes || vrTrailingZeroes) { // General case, which happens rarely (~4.0%) while ((p10 = vp / 10U) > (m10 = vm.DivMod(10U, out uint vmRem))) { if (vmRem != 0U) { vmTrailingZeroes = false; } if (removedDigit != 0U) { vrTrailingZeroes = false; } vr = vr.DivMod(10U, out removedDigit); vp = p10; vm = m10; removed++; } if (vmTrailingZeroes) { while ((vm - 10U * (m10 = vm / 10U)) == 0U) { if (removedDigit != 0U) { vrTrailingZeroes = false; } vr = vr.DivMod(10U, out removedDigit); vp /= 10U; vm = m10; removed++; } } if (vrTrailingZeroes && removedDigit == 5U && vr % 2U == 0U) { // Round even if the exact number is .....50..0 removedDigit = 4U; } // We need to take vr + 1 if vr is outside bounds or we need to round up output = vr; if ((vr == vm && (!acceptBounds || !vmTrailingZeroes)) || removedDigit >= 5U) { output++; } } else { // Specialized for the common case (~96.0%). Percentages below are relative to // this. Loop iterations below (approximately): // 0: 13.6%, 1: 70.7%, 2: 14.1%, 3: 1.39%, 4: 0.14%, 5+: 0.01% while (RyuUtils.DivCompare10(ref vp, ref vm)) { vr = vr.DivMod(10U, out removedDigit); removed++; } // We need to take vr + 1 if vr is outside bounds or we need to round up output = vr; if (vr == vm || removedDigit >= 5) { output++; } } sign = fSign; this.mantissa = output; this.exponent = e10 + removed; }
/// <summary> /// Convert a 64-bit floating point number to an exponential notation string. /// </summary> internal static void ToExponentialString(StringBuilder result, ulong ieeeMantissa, uint ieeeExponent, int precision, RyuFormatOptions options, NumberFormatInfo info) { bool printDP = precision > 0, soft = (options & RyuFormatOptions.SoftPrecision) != 0; uint digits = 0U; int printedDigits = 0, availDigits = 0, exp = 0, exponent = Decode(ieeeMantissa, ieeeExponent, out ulong mantissa), start = result.Length; ulong mantShift = mantissa << MANTISSA_SHIFT; ++precision; if (exponent >= -RyuFloat64.DOUBLE_MANTISSA_BITS) { int idx = (exponent < 0) ? 0 : RyuUtils.IndexForExponent(exponent), i = RyuUtils.LengthForIndex(idx) - 1, j = Pow10BitsForIndex(idx) - exponent + MANTISSA_SHIFT, p = RyuTables.POW10_OFFSET_D[idx] + i; for (; i >= 0; i--) { // Temporary: j is usually around 128, and by shifting a bit, we push it // to 128 or above, which is a slightly faster code path in // MulShiftMod1E9. Instead, we can just increase the multipliers digits = RyuUtils.MulShiftMod1E9(mantShift, RyuTables.POW10_SPLIT_D[p, 0], RyuTables.POW10_SPLIT_D[p, 1], RyuTables.POW10_SPLIT_D[p, 2], j); if (printedDigits > 0) { if (printedDigits + 9 > precision) { availDigits = 9; break; } RyuUtils.Append9Digits(result, digits); printedDigits += 9; } else if (digits != 0U) { availDigits = RyuUtils.DecimalLength9(digits); exp = i * 9 + availDigits - 1; if (availDigits > precision) { break; } RyuUtils.AppendDDigits(result, digits, availDigits + 1, printDP, info); printedDigits = availDigits; availDigits = 0; } p--; } } if (exponent < 0 && availDigits == 0) { int idx = (-exponent) >> 4, pMax = RyuTables.POW10_OFFSET_2_D[idx + 1], p = RyuTables.POW10_OFFSET_2_D[idx], j = MANTISSA_SHIFT + POW10_ADDITIONAL_BITS - exponent - (idx << 4); for (int i = RyuTables.MIN_BLOCK_2_D[idx]; i < 200; i++) { digits = (p >= pMax) ? 0U : RyuUtils.MulShiftMod1E9(mantShift, RyuTables.POW10_SPLIT_2_D[p, 0], RyuTables.POW10_SPLIT_2_D[p, 1], RyuTables.POW10_SPLIT_2_D[p, 2], j); if (printedDigits > 0) { if (printedDigits + 9 > precision) { availDigits = 9; break; } RyuUtils.Append9Digits(result, digits); printedDigits += 9; } else if (digits != 0) { availDigits = RyuUtils.DecimalLength9(digits); exp = (i + 1) * -9 + availDigits - 1; if (availDigits > precision) { break; } RyuUtils.AppendDDigits(result, digits, availDigits + 1, printDP, info); printedDigits = availDigits; availDigits = 0; } p++; } } // 0 = don't round up; 1 = round up unconditionally; 2 = round up if odd int maxDigits = precision - printedDigits, roundFlag; uint lastDigit = 0U; if (availDigits == 0) { digits = 0U; } if (availDigits > maxDigits) { lastDigit = RyuUtils.LastDigit(ref digits, availDigits - maxDigits); } if (lastDigit != 5U) { roundFlag = (lastDigit > 5U) ? 1 : 0; } else { // Is m * 2^e2 * 10^(precision + 1 - exp) integer? // precision was already increased by 1, so we don't need to write + 1 here. int rexp = precision - exp; bool trailingZeroes = HasTrailingZeroes(exponent, rexp, mantissa); if (rexp < 0 && trailingZeroes) { trailingZeroes = RyuUtils.IsMultipleOf5Power(mantissa, -rexp); } roundFlag = trailingZeroes ? 2 : 1; } if (printedDigits > 0) { if (digits == 0U) { if (!soft) { RyuUtils.Append0(result, maxDigits); } } else { RyuUtils.AppendCDigits(result, digits, maxDigits); } } else { RyuUtils.AppendDDigits(result, digits, maxDigits + 1, printDP, info); } if (roundFlag != 0 && RyuUtils.RoundResult(result, start, roundFlag, out _, info)) { exp++; } if (soft) { RyuUtils.SoftenResult(result, info); } RyuUtils.AppendExponent(result, exp, options, info); }
/// <summary> /// Convert a 64-bit floating point number to a fixed notation string. /// </summary> internal static void ToFixedString(StringBuilder result, ulong ieeeMantissa, uint ieeeExponent, int precision, RyuFormatOptions options, NumberFormatInfo info) { bool zero = true, soft = (options & RyuFormatOptions.SoftPrecision) != 0; int exponent = Decode(ieeeMantissa, ieeeExponent, out ulong mantissa), start = result.Length; ulong mShift = mantissa << MANTISSA_SHIFT; uint digits; if (exponent >= -RyuFloat64.DOUBLE_MANTISSA_BITS) { int idx = (exponent < 0) ? 0 : RyuUtils.IndexForExponent(exponent), p10bits = Pow10BitsForIndex(idx), i = RyuUtils.LengthForIndex(idx) - 1, p = RyuTables.POW10_OFFSET_D[idx] + i, j = p10bits - exponent + MANTISSA_SHIFT; for (; i >= 0; i--) { digits = RyuUtils.MulShiftMod1E9(mShift, RyuTables.POW10_SPLIT_D[p, 0], RyuTables.POW10_SPLIT_D[p, 1], RyuTables.POW10_SPLIT_D[p, 2], j); if (!zero) { RyuUtils.Append9Digits(result, digits); } else if (digits != 0U) { RyuUtils.AppendNDigits(result, RyuUtils.DecimalLength9(digits), digits); zero = false; } p--; } } if (zero) { result.Append(RyuUtils.ZERO); } if ((options & RyuFormatOptions.ThousandsSeparators) != 0) { RyuUtils.AddThousands(result, start, info); } if (precision > 0 && (!soft || exponent < 0)) { result.Append(info.NumberDecimalSeparator); } if (exponent < 0) { // 0 = don't round up; 1 = round up unconditionally; 2 = round up if odd int idx = (-exponent) >> 4, roundFlag = 0, i = 0, blocks = precision / 9 + 1, minBlock = RyuTables.MIN_BLOCK_2_D[idx]; if (blocks <= minBlock) { RyuUtils.Append0(result, precision); i = blocks; } else if (i < minBlock) { RyuUtils.Append0(result, 9 * minBlock); i = minBlock; } int p = RyuTables.POW10_OFFSET_2_D[idx] + i - minBlock, pMax = RyuTables. POW10_OFFSET_2_D[idx + 1], j = MANTISSA_SHIFT + POW10_ADDITIONAL_BITS - exponent - (idx << 4); for (; i < blocks; ++i) { if (p >= pMax) { // If the remaining digits are all 0, then no rounding required if (!soft) { RyuUtils.Append0(result, precision - 9 * i); } break; } digits = RyuUtils.MulShiftMod1E9(mShift, RyuTables.POW10_SPLIT_2_D[p, 0], RyuTables.POW10_SPLIT_2_D[p, 1], RyuTables.POW10_SPLIT_2_D[p, 2], j); if (i < blocks - 1) { RyuUtils.Append9Digits(result, digits); } else { int maximum = precision - 9 * i; uint lastDigit = RyuUtils.LastDigit(ref digits, 9 - maximum); // Is m * 10^(additionalDigits + 1) / 2^(-e2) integer? if (lastDigit > 5U) { roundFlag = 1; } else if (lastDigit < 5U) { roundFlag = 0; } else if (HasTrailingZeroes(exponent, precision + 1, mantissa)) { roundFlag = 2; } else { roundFlag = 1; } if (maximum > 0) { RyuUtils.AppendCDigits(result, digits, maximum); } break; } p++; } if (roundFlag != 0 && RyuUtils.RoundResult(result, start, roundFlag, out int decimalIndex, info)) { if (decimalIndex > 0) { result[decimalIndex++] = RyuUtils.ZERO; result[decimalIndex] = info.NumberDecimalSeparator[0]; } result.Append(RyuUtils.ZERO); } if (soft && precision > 0) { RyuUtils.SoftenResult(result, info); } } else if (!soft) { RyuUtils.Append0(result, precision); } }