Пример #1
0
        // This routine multiplies numerator/denominator so that its values lies in the
        // range 1-10. That is after a call to this function we have:
        //    1 <= (numerator + delta_plus) /denominator < 10.
        // Let numerator the input before modification and numerator' the argument
        // after modification, then the output-parameter decimal_point is such that
        //  numerator / denominator * 10^estimated_power ==
        //    numerator' / denominator' * 10^(decimal_point - 1)
        // In some cases estimated_power was too low, and this is already the case. We
        // then simply adjust the power so that 10^(k-1) <= v < 10^k (with k ==
        // estimated_power) but do not touch the numerator or denominator.
        // Otherwise the routine multiplies the numerator and the deltas by 10.
        private static void FixupMultiply10(
            int estimated_power,
            bool is_even,
            out int decimal_point,
            Bignum numerator,
            Bignum denominator,
            Bignum delta_minus,
            Bignum delta_plus)
        {
            bool in_range;

            if (is_even)
            {
                in_range = Bignum.PlusCompare(numerator, delta_plus, denominator) >= 0;
            }
            else
            {
                in_range = Bignum.PlusCompare(numerator, delta_plus, denominator) > 0;
            }
            if (in_range)
            {
                // Since numerator + delta_plus >= denominator we already have
                // 1 <= numerator/denominator < 10. Simply update the estimated_power.
                decimal_point = estimated_power + 1;
            }
            else
            {
                decimal_point = estimated_power;
                numerator.Times10();
                if (Bignum.Equal(delta_minus, delta_plus))
                {
                    delta_minus.Times10();
                    delta_plus.AssignBignum(delta_minus);
                }
                else
                {
                    delta_minus.Times10();
                    delta_plus.Times10();
                }
            }
        }
Пример #2
0
        // Generates 'requested_digits' after the decimal point. It might omit
        // trailing '0's. If the input number is too small then no digits at all are
        // generated (ex.: 2 fixed digits for 0.00001).
        //
        // Input verifies:  1 <= (numerator + delta) / denominator < 10.
        static void BignumToFixed(
            int requested_digits,
            ref int decimal_point,
            Bignum numerator,
            Bignum denominator,
            DtoaBuilder buffer)
        {
            // Note that we have to look at more than just the requested_digits, since
            // a number could be rounded up. Example: v=0.5 with requested_digits=0.
            // Even though the power of v equals 0 we can't just stop here.
            if (-(decimal_point) > requested_digits)
            {
                // The number is definitively too small.
                // Ex: 0.001 with requested_digits == 1.
                // Set decimal-point to -requested_digits. This is what Gay does.
                // Note that it should not have any effect anyways since the string is
                // empty.
                decimal_point = -requested_digits;
                buffer.Reset();
                return;
            }

            if (-decimal_point == requested_digits)
            {
                // We only need to verify if the number rounds down or up.
                // Ex: 0.04 and 0.06 with requested_digits == 1.
                Debug.Assert(decimal_point == -requested_digits);
                // Initially the fraction lies in range (1, 10]. Multiply the denominator
                // by 10 so that we can compare more easily.
                denominator.Times10();
                if (Bignum.PlusCompare(numerator, numerator, denominator) >= 0)
                {
                    // If the fraction is >= 0.5 then we have to include the rounded
                    // digit.
                    buffer[0] = '1';
                    decimal_point++;
                }
                else
                {
                    // Note that we caught most of similar cases earlier.
                    buffer.Reset();
                }
            }
            else
            {
                // The requested digits correspond to the digits after the point.
                // The variable 'needed_digits' includes the digits before the point.
                int needed_digits = (decimal_point) + requested_digits;
                GenerateCountedDigits(needed_digits, ref decimal_point, numerator, denominator, buffer);
            }
        }
Пример #3
0
        // 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++;
            }
        }
Пример #4
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;
                }
            }
        }