Beispiel #1
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;
                }
            }
        }