Example #1
0
        /// <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);
            }
        }
Example #2
0
        private static bool HasTrailingZeroes(int exponent, int rexp, ulong mantissa)
        {
            int requiredTwos = -exponent - rexp;

            return(requiredTwos <= 0 || (requiredTwos < 60 && RyuUtils.IsMultipleOf2Power(
                                             mantissa, requiredTwos)));
        }
Example #3
0
        /// <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);
        }
Example #4
0
        /// <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);
        }
Example #5
0
        /// <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);
        }
Example #6
0
        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;
        }
Example #7
0
        /// <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);
        }
Example #8
0
        /// <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;
        }
Example #9
0
        /// <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);
        }
Example #10
0
        /// <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);
            }
        }