// 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; } } }