// Let v = numerator / denominator < 10.
        // Then we generate 'count' digits of d = x.xxxxx... (without the decimal point)
        // from left to right. Once 'count' digits have been produced we decide wether
        // to round up or down. Remainders of exactly .5 round upwards. Numbers such
        // as 9.999999 propagate a carry all the way, and change the
        // exponent (decimal_point), when rounding upwards.
        static void GenerateCountedDigits(
            int count,
            ref int decimal_point,
            Bignum numerator,
            Bignum denominator,
            DtoaBuilder buffer)
        {
            Debug.Assert(count >= 0);
            for (int i = 0; i < count - 1; ++i)
            {
                uint d = numerator.DivideModuloIntBignum(denominator);
                Debug.Assert(d <= 9);  // digit is a uint and therefore always positive.
                // digit = numerator / denominator (integer division).
                // numerator = numerator % denominator.
                buffer.Append((char)(d + '0'));
                // Prepare for next iteration.
                numerator.Times10();
            }
            // Generate the last digit.
            uint digit = numerator.DivideModuloIntBignum(denominator);

            if (Bignum.PlusCompare(numerator, numerator, denominator) >= 0)
            {
                digit++;
            }
            buffer.Append((char)(digit + '0'));
            // Correct bad digits (in case we had a sequence of '9's). Propagate the
            // carry until we hat a non-'9' or til we reach the first digit.
            for (int i = count - 1; i > 0; --i)
            {
                if (buffer[i] != '0' + 10)
                {
                    break;
                }
                buffer[i] = '0';
                buffer[i - 1]++;
            }
            if (buffer[0] == '0' + 10)
            {
                // Propagate a carry past the top place.
                buffer[0] = '1';
                decimal_point++;
            }
        }
Beispiel #2
0
        // The procedure starts generating digits from the left to the right and stops
        // when the generated digits yield the shortest decimal representation of v. A
        // decimal representation of v is a number lying closer to v than to any other
        // double, so it converts to v when read.
        //
        // This is true if d, the decimal representation, is between m- and m+, the
        // upper and lower boundaries. d must be strictly between them if !is_even.
        //           m- := (numerator - delta_minus) / denominator
        //           m+ := (numerator + delta_plus) / denominator
        //
        // Precondition: 0 <= (numerator+delta_plus) / denominator < 10.
        //   If 1 <= (numerator+delta_plus) / denominator < 10 then no leading 0 digit
        //   will be produced. This should be the standard precondition.
        private static void GenerateShortestDigits(
            Bignum numerator,
            Bignum denominator,
            Bignum delta_minus,
            Bignum delta_plus,
            bool is_even,
            DtoaBuilder buffer)
        {
            // Small optimization: if delta_minus and delta_plus are the same just reuse
            // one of the two bignums.
            if (Bignum.Equal(delta_minus, delta_plus))
            {
                delta_plus = delta_minus;
            }

            buffer.Reset();
            while (true)
            {
                uint digit;
                digit = numerator.DivideModuloIntBignum(denominator);
                // digit = numerator / denominator (integer division).
                // numerator = numerator % denominator.
                buffer.Append((char)(digit + '0'));

                // Can we stop already?
                // If the remainder of the division is less than the distance to the lower
                // boundary we can stop. In this case we simply round down (discarding the
                // remainder).
                // Similarly we test if we can round up (using the upper boundary).
                bool in_delta_room_minus;
                bool in_delta_room_plus;
                if (is_even)
                {
                    in_delta_room_minus = Bignum.LessEqual(numerator, delta_minus);
                }
                else
                {
                    in_delta_room_minus = Bignum.Less(numerator, delta_minus);
                }
                if (is_even)
                {
                    in_delta_room_plus = Bignum.PlusCompare(numerator, delta_plus, denominator) >= 0;
                }
                else
                {
                    in_delta_room_plus = Bignum.PlusCompare(numerator, delta_plus, denominator) > 0;
                }
                if (!in_delta_room_minus && !in_delta_room_plus)
                {
                    // Prepare for next iteration.
                    numerator.Times10();
                    delta_minus.Times10();
                    // We optimized delta_plus to be equal to delta_minus (if they share the
                    // same value). So don't multiply delta_plus if they point to the same
                    // object.
                    if (delta_minus != delta_plus)
                    {
                        delta_plus.Times10();
                    }
                }
                else if (in_delta_room_minus && in_delta_room_plus)
                {
                    // Let's see if 2*numerator < denominator.
                    // If yes, then the next digit would be < 5 and we can round down.
                    int compare = Bignum.PlusCompare(numerator, numerator, denominator);
                    if (compare < 0)
                    {
                        // Remaining digits are less than .5. -> Round down (== do nothing).
                    }
                    else if (compare > 0)
                    {
                        // Remaining digits are more than .5 of denominator. . Round up.
                        // Note that the last digit could not be a '9' as otherwise the whole
                        // loop would have stopped earlier.
                        // We still have an assert here in case the preconditions were not
                        // satisfied.
                        buffer[buffer.Length - 1]++;
                    }
                    else
                    {
                        // Halfway case.
                        // TODO(floitsch): need a way to solve half-way cases.
                        //   For now let's round towards even (since this is what Gay seems to
                        //   do).

                        if ((buffer[buffer.Length - 1] - '0') % 2 == 0)
                        {
                            // Round down => Do nothing.
                        }
                        else
                        {
                            buffer[buffer.Length - 1]++;
                        }
                    }

                    return;
                }
                else if (in_delta_room_minus)
                {
                    // Round down (== do nothing).
                    return;
                }
                else
                {
                    // in_delta_room_plus
                    // Round up.
                    // Note again that the last digit could not be '9' since this would have
                    // stopped the loop earlier.
                    // We still have an DCHECK here, in case the preconditions were not
                    // satisfied.
                    buffer[buffer.Length - 1]++;
                    return;
                }
            }
        }