From: floitschV8@gmail.com Date: Wed, 17 Nov 2010 13:20:44 +0000 (+0000) Subject: Add bignum fall-back when the fast dtoa doesn't succeed. This removes Gay's dtoa... X-Git-Tag: upstream/4.7.83~20945 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=7ac932c08876d49da2feb88fe7d1d265baff9613;p=platform%2Fupstream%2Fv8.git Add bignum fall-back when the fast dtoa doesn't succeed. This removes Gay's dtoa for the double->string direction. We still need it for the string->double direction. Review URL: http://codereview.chromium.org/3468003 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@5840 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- diff --git a/src/SConscript b/src/SConscript index b18498f..9c6d992 100755 --- a/src/SConscript +++ b/src/SConscript @@ -41,6 +41,7 @@ SOURCES = { assembler.cc ast.cc bignum.cc + bignum-dtoa.cc bootstrapper.cc builtins.cc cached-powers.cc diff --git a/src/bignum-dtoa.cc b/src/bignum-dtoa.cc new file mode 100644 index 0000000..a48bbb7 --- /dev/null +++ b/src/bignum-dtoa.cc @@ -0,0 +1,655 @@ +// Copyright 2010 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include + +#include "v8.h" +#include "bignum-dtoa.h" + +#include "bignum.h" +#include "double.h" + +namespace v8 { +namespace internal { + +static int NormalizedExponent(uint64_t significand, int exponent) { + ASSERT(significand != 0); + while ((significand & Double::kHiddenBit) == 0) { + significand = significand << 1; + exponent = exponent - 1; + } + return exponent; +} + + +// Forward declarations: +// Returns an estimation of k such that 10^(k-1) <= v < 10^k. +static int EstimatePower(int); +// Computes v / 10^estimated_power exactly, as a ratio of two bignums, numerator +// and denominator. +static void InitialScaledStartValues(double v, + int estimated_power, + bool need_boundary_deltas, + Bignum* numerator, + Bignum* denominator, + Bignum* delta_minus, + Bignum* delta_plus); +// Multiplies numerator/denominator so that its values lies in the range 1-10. +// Returns decimal_point s.t. +// v = numerator'/denominator' * 10^(decimal_point-1) +// where numerator' and denominator' are the values of numerator and +// denominator after the call to this function. +static void FixupMultiply10(int estimated_power, bool is_even, + int* decimal_point, + Bignum* numerator, Bignum* denominator, + Bignum* delta_minus, Bignum* delta_plus); +// Generates digits from the left to the right and stops when the generated +// digits yield the shortest decimal representation of v. +static void GenerateShortestDigits(Bignum* numerator, Bignum* denominator, + Bignum* delta_minus, Bignum* delta_plus, + bool is_even, + Vector buffer, int* length); +// Generates 'requested_digits' after the decimal point. +static void BignumToFixed(int requested_digits, int* decimal_point, + Bignum* numerator, Bignum* denominator, + Vector(buffer), int* length); +// Generates 'count' digits of numerator/denominator. +// Once 'count' digits have been produced rounds the result depending on the +// remainder (remainders of exactly .5 round upwards). Might update the +// decimal_point when rounding up (for example for 0.9999). +static void GenerateCountedDigits(int count, int* decimal_point, + Bignum* numerator, Bignum* denominator, + Vector(buffer), int* length); + + +void BignumDtoa(double v, BignumDtoaMode mode, int requested_digits, + Vector buffer, int* length, int* decimal_point) { + ASSERT(v > 0); + ASSERT(!Double(v).IsSpecial()); + uint64_t significand = Double(v).Significand(); + bool is_even = (significand & 1) == 0; + int exponent = Double(v).Exponent(); + int normalized_exponent = NormalizedExponent(significand, exponent); + // estimated_power might be too low by 1. + int estimated_power = EstimatePower(normalized_exponent); + + // Shortcut for Fixed. + // The requested digits correspond to the digits after the point. If the + // number is much too small, then there is no need in trying to get any + // digits. + if (mode == BIGNUM_DTOA_FIXED && -estimated_power - 1 > requested_digits) { + buffer[0] = '\0'; + *length = 0; + // 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; + return; + } + + Bignum numerator; + Bignum denominator; + Bignum delta_minus; + Bignum delta_plus; + // Make sure the bignum can grow large enough. The smallest double equals + // 4e-324. In this case the denominator needs fewer than 324*4 binary digits. + // The maximum double is 1.7976931348623157e308 which needs fewer than + // 308*4 binary digits. + ASSERT(Bignum::kMaxSignificantBits >= 324*4); + bool need_boundary_deltas = (mode == BIGNUM_DTOA_SHORTEST); + InitialScaledStartValues(v, estimated_power, need_boundary_deltas, + &numerator, &denominator, + &delta_minus, &delta_plus); + // We now have v = (numerator / denominator) * 10^estimated_power. + FixupMultiply10(estimated_power, is_even, decimal_point, + &numerator, &denominator, + &delta_minus, &delta_plus); + // We now have v = (numerator / denominator) * 10^(decimal_point-1), and + // 1 <= (numerator + delta_plus) / denominator < 10 + switch (mode) { + case BIGNUM_DTOA_SHORTEST: + GenerateShortestDigits(&numerator, &denominator, + &delta_minus, &delta_plus, + is_even, buffer, length); + break; + case BIGNUM_DTOA_FIXED: + BignumToFixed(requested_digits, decimal_point, + &numerator, &denominator, + buffer, length); + break; + case BIGNUM_DTOA_PRECISION: + GenerateCountedDigits(requested_digits, decimal_point, + &numerator, &denominator, + buffer, length); + break; + default: + UNREACHABLE(); + } + buffer[*length] = '\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. +static void GenerateShortestDigits(Bignum* numerator, Bignum* denominator, + Bignum* delta_minus, Bignum* delta_plus, + bool is_even, + Vector buffer, int* length) { + // 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; + } + *length = 0; + while (true) { + uint16_t digit; + digit = numerator->DivideModuloIntBignum(*denominator); + ASSERT(digit <= 9); // digit is a uint16_t and therefore always positive. + // digit = numerator / denominator (integer division). + // numerator = numerator % denominator. + buffer[(*length)++] = 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. + ASSERT(buffer[(*length) - 1] != '9'); + 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[(*length) - 1] - '0') % 2 == 0) { + // Round down => Do nothing. + } else { + ASSERT(buffer[(*length) - 1] != '9'); + 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 ASSERT here, in case the preconditions were not + // satisfied. + ASSERT(buffer[(*length) -1] != '9'); + buffer[(*length) - 1]++; + return; + } + } +} + + +// 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, int* decimal_point, + Bignum* numerator, Bignum* denominator, + Vector(buffer), int* length) { + ASSERT(count >= 0); + for (int i = 0; i < count - 1; ++i) { + uint16_t digit; + digit = numerator->DivideModuloIntBignum(*denominator); + ASSERT(digit <= 9); // digit is a uint16_t and therefore always positive. + // digit = numerator / denominator (integer division). + // numerator = numerator % denominator. + buffer[i] = digit + '0'; + // Prepare for next iteration. + numerator->Times10(); + } + // Generate the last digit. + uint16_t digit; + digit = numerator->DivideModuloIntBignum(*denominator); + if (Bignum::PlusCompare(*numerator, *numerator, *denominator) >= 0) { + digit++; + } + buffer[count - 1] = 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)++; + } + *length = count; +} + + +// 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, int* decimal_point, + Bignum* numerator, Bignum* denominator, + Vector(buffer), int* length) { + // 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; + *length = 0; + return; + } else 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. + 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'; + *length = 1; + (*decimal_point)++; + } else { + // Note that we caught most of similar cases earlier. + *length = 0; + } + return; + } 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, decimal_point, + numerator, denominator, + buffer, length); + } +} + + +// Returns an estimation of k such that 10^(k-1) <= v < 10^k where +// v = f * 2^exponent and 2^52 <= f < 2^53. +// v is hence a normalized double with the given exponent. The output is an +// approximation for the exponent of the decimal approimation .digits * 10^k. +// +// The result might undershoot by 1 in which case 10^k <= v < 10^k+1. +// Note: this property holds for v's upper boundary m+ too. +// 10^k <= m+ < 10^k+1. +// (see explanation below). +// +// Examples: +// EstimatePower(0) => 16 +// EstimatePower(-52) => 0 +// +// Note: e >= 0 => EstimatedPower(e) > 0. No similar claim can be made for e<0. +static int EstimatePower(int exponent) { + // This function estimates log10 of v where v = f*2^e (with e == exponent). + // Note that 10^floor(log10(v)) <= v, but v <= 10^ceil(log10(v)). + // Note that f is bounded by its container size. Let p = 53 (the double's + // significand size). Then 2^(p-1) <= f < 2^p. + // + // Given that log10(v) == log2(v)/log2(10) and e+(len(f)-1) is quite close + // to log2(v) the function is simplified to (e+(len(f)-1)/log2(10)). + // The computed number undershoots by less than 0.631 (when we compute log3 + // and not log10). + // + // Optimization: since we only need an approximated result this computation + // can be performed on 64 bit integers. On x86/x64 architecture the speedup is + // not really measurable, though. + // + // Since we want to avoid overshooting we decrement by 1e10 so that + // floating-point imprecisions don't affect us. + // + // Explanation for v's boundary m+: the computation takes advantage of + // the fact that 2^(p-1) <= f < 2^p. Boundaries still satisfy this requirement + // (even for denormals where the delta can be much more important). + + const double k1Log10 = 0.30102999566398114; // 1/lg(10) + + // For doubles len(f) == 53 (don't forget the hidden bit). + const int kSignificandSize = 53; + double estimate = ceil((exponent + kSignificandSize - 1) * k1Log10 - 1e-10); + return static_cast(estimate); +} + + +// See comments for InitialScaledStartValues. +static void InitialScaledStartValuesPositiveExponent( + double v, int estimated_power, bool need_boundary_deltas, + Bignum* numerator, Bignum* denominator, + Bignum* delta_minus, Bignum* delta_plus) { + // A positive exponent implies a positive power. + ASSERT(estimated_power >= 0); + // Since the estimated_power is positive we simply multiply the denominator + // by 10^estimated_power. + + // numerator = v. + numerator->AssignUInt64(Double(v).Significand()); + numerator->ShiftLeft(Double(v).Exponent()); + // denominator = 10^estimated_power. + denominator->AssignPowerUInt16(10, estimated_power); + + if (need_boundary_deltas) { + // Introduce a common denominator so that the deltas to the boundaries are + // integers. + denominator->ShiftLeft(1); + numerator->ShiftLeft(1); + // Let v = f * 2^e, then m+ - v = 1/2 * 2^e; With the common + // denominator (of 2) delta_plus equals 2^e. + delta_plus->AssignUInt16(1); + delta_plus->ShiftLeft(Double(v).Exponent()); + // Same for delta_minus (with adjustments below if f == 2^p-1). + delta_minus->AssignUInt16(1); + delta_minus->ShiftLeft(Double(v).Exponent()); + + // If the significand (without the hidden bit) is 0, then the lower + // boundary is closer than just half a ulp (unit in the last place). + // There is only one exception: if the next lower number is a denormal then + // the distance is 1 ulp. This cannot be the case for exponent >= 0 (but we + // have to test it in the other function where exponent < 0). + uint64_t v_bits = Double(v).AsUint64(); + if ((v_bits & Double::kSignificandMask) == 0) { + // The lower boundary is closer at half the distance of "normal" numbers. + // Increase the common denominator and adapt all but the delta_minus. + denominator->ShiftLeft(1); // *2 + numerator->ShiftLeft(1); // *2 + delta_plus->ShiftLeft(1); // *2 + } + } +} + + +// See comments for InitialScaledStartValues +static void InitialScaledStartValuesNegativeExponentPositivePower( + double v, int estimated_power, bool need_boundary_deltas, + Bignum* numerator, Bignum* denominator, + Bignum* delta_minus, Bignum* delta_plus) { + uint64_t significand = Double(v).Significand(); + int exponent = Double(v).Exponent(); + // v = f * 2^e with e < 0, and with estimated_power >= 0. + // This means that e is close to 0 (have a look at how estimated_power is + // computed). + + // numerator = significand + // since v = significand * 2^exponent this is equivalent to + // numerator = v * / 2^-exponent + numerator->AssignUInt64(significand); + // denominator = 10^estimated_power * 2^-exponent (with exponent < 0) + denominator->AssignPowerUInt16(10, estimated_power); + denominator->ShiftLeft(-exponent); + + if (need_boundary_deltas) { + // Introduce a common denominator so that the deltas to the boundaries are + // integers. + denominator->ShiftLeft(1); + numerator->ShiftLeft(1); + // Let v = f * 2^e, then m+ - v = 1/2 * 2^e; With the common + // denominator (of 2) delta_plus equals 2^e. + // Given that the denominator already includes v's exponent the distance + // to the boundaries is simply 1. + delta_plus->AssignUInt16(1); + // Same for delta_minus (with adjustments below if f == 2^p-1). + delta_minus->AssignUInt16(1); + + // If the significand (without the hidden bit) is 0, then the lower + // boundary is closer than just one ulp (unit in the last place). + // There is only one exception: if the next lower number is a denormal + // then the distance is 1 ulp. Since the exponent is close to zero + // (otherwise estimated_power would have been negative) this cannot happen + // here either. + uint64_t v_bits = Double(v).AsUint64(); + if ((v_bits & Double::kSignificandMask) == 0) { + // The lower boundary is closer at half the distance of "normal" numbers. + // Increase the denominator and adapt all but the delta_minus. + denominator->ShiftLeft(1); // *2 + numerator->ShiftLeft(1); // *2 + delta_plus->ShiftLeft(1); // *2 + } + } +} + + +// See comments for InitialScaledStartValues +static void InitialScaledStartValuesNegativeExponentNegativePower( + double v, int estimated_power, bool need_boundary_deltas, + Bignum* numerator, Bignum* denominator, + Bignum* delta_minus, Bignum* delta_plus) { + const uint64_t kMinimalNormalizedExponent = + V8_2PART_UINT64_C(0x00100000, 00000000); + uint64_t significand = Double(v).Significand(); + int exponent = Double(v).Exponent(); + // Instead of multiplying the denominator with 10^estimated_power we + // multiply all values (numerator and deltas) by 10^-estimated_power. + + // Use numerator as temporary container for power_ten. + Bignum* power_ten = numerator; + power_ten->AssignPowerUInt16(10, -estimated_power); + + if (need_boundary_deltas) { + // Since power_ten == numerator we must make a copy of 10^estimated_power + // before we complete the computation of the numerator. + // delta_plus = delta_minus = 10^estimated_power + delta_plus->AssignBignum(*power_ten); + delta_minus->AssignBignum(*power_ten); + } + + // numerator = significand * 2 * 10^-estimated_power + // since v = significand * 2^exponent this is equivalent to + // numerator = v * 10^-estimated_power * 2 * 2^-exponent. + // Remember: numerator has been abused as power_ten. So no need to assign it + // to itself. + ASSERT(numerator == power_ten); + numerator->MultiplyByUInt64(significand); + + // denominator = 2 * 2^-exponent with exponent < 0. + denominator->AssignUInt16(1); + denominator->ShiftLeft(-exponent); + + if (need_boundary_deltas) { + // Introduce a common denominator so that the deltas to the boundaries are + // integers. + numerator->ShiftLeft(1); + denominator->ShiftLeft(1); + // With this shift the boundaries have their correct value, since + // delta_plus = 10^-estimated_power, and + // delta_minus = 10^-estimated_power. + // These assignments have been done earlier. + + // The special case where the lower boundary is twice as close. + // This time we have to look out for the exception too. + uint64_t v_bits = Double(v).AsUint64(); + if ((v_bits & Double::kSignificandMask) == 0 && + // The only exception where a significand == 0 has its boundaries at + // "normal" distances: + (v_bits & Double::kExponentMask) != kMinimalNormalizedExponent) { + numerator->ShiftLeft(1); // *2 + denominator->ShiftLeft(1); // *2 + delta_plus->ShiftLeft(1); // *2 + } + } +} + + +// Let v = significand * 2^exponent. +// Computes v / 10^estimated_power exactly, as a ratio of two bignums, numerator +// and denominator. The functions GenerateShortestDigits and +// GenerateCountedDigits will then convert this ratio to its decimal +// representation d, with the required accuracy. +// Then d * 10^estimated_power is the representation of v. +// (Note: the fraction and the estimated_power might get adjusted before +// generating the decimal representation.) +// +// The initial start values consist of: +// - a scaled numerator: s.t. numerator/denominator == v / 10^estimated_power. +// - a scaled (common) denominator. +// optionally (used by GenerateShortestDigits to decide if it has the shortest +// decimal converting back to v): +// - v - m-: the distance to the lower boundary. +// - m+ - v: the distance to the upper boundary. +// +// v, m+, m-, and therefore v - m- and m+ - v all share the same denominator. +// +// Let ep == estimated_power, then the returned values will satisfy: +// v / 10^ep = numerator / denominator. +// v's boundarys m- and m+: +// m- / 10^ep == v / 10^ep - delta_minus / denominator +// m+ / 10^ep == v / 10^ep + delta_plus / denominator +// Or in other words: +// m- == v - delta_minus * 10^ep / denominator; +// m+ == v + delta_plus * 10^ep / denominator; +// +// Since 10^(k-1) <= v < 10^k (with k == estimated_power) +// or 10^k <= v < 10^(k+1) +// we then have 0.1 <= numerator/denominator < 1 +// or 1 <= numerator/denominator < 10 +// +// It is then easy to kickstart the digit-generation routine. +// +// The boundary-deltas are only filled if need_boundary_deltas is set. +static void InitialScaledStartValues(double v, + int estimated_power, + bool need_boundary_deltas, + Bignum* numerator, + Bignum* denominator, + Bignum* delta_minus, + Bignum* delta_plus) { + if (Double(v).Exponent() >= 0) { + InitialScaledStartValuesPositiveExponent( + v, estimated_power, need_boundary_deltas, + numerator, denominator, delta_minus, delta_plus); + } else if (estimated_power >= 0) { + InitialScaledStartValuesNegativeExponentPositivePower( + v, estimated_power, need_boundary_deltas, + numerator, denominator, delta_minus, delta_plus); + } else { + InitialScaledStartValuesNegativeExponentNegativePower( + v, estimated_power, need_boundary_deltas, + numerator, denominator, delta_minus, delta_plus); + } +} + + +// 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. +static void FixupMultiply10(int estimated_power, bool is_even, + int* decimal_point, + Bignum* numerator, Bignum* denominator, + Bignum* delta_minus, Bignum* delta_plus) { + bool in_range; + if (is_even) { + // For IEEE doubles half-way cases (in decimal system numbers ending with 5) + // are rounded to the closest floating-point number with even significand. + 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(); + } + } +} + +} } // namespace v8::internal diff --git a/src/bignum-dtoa.h b/src/bignum-dtoa.h new file mode 100644 index 0000000..ea1acbb --- /dev/null +++ b/src/bignum-dtoa.h @@ -0,0 +1,81 @@ +// Copyright 2010 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef V8_BIGNUM_DTOA_H_ +#define V8_BIGNUM_DTOA_H_ + +namespace v8 { +namespace internal { + +enum BignumDtoaMode { + // Return the shortest correct representation. + // For example the output of 0.299999999999999988897 is (the less accurate but + // correct) 0.3. + BIGNUM_DTOA_SHORTEST, + // Return a fixed number of digits after the decimal point. + // For instance fixed(0.1, 4) becomes 0.1000 + // If the input number is big, the output will be big. + BIGNUM_DTOA_FIXED, + // Return a fixed number of digits, no matter what the exponent is. + BIGNUM_DTOA_PRECISION +}; + +// Converts the given double 'v' to ascii. +// The result should be interpreted as buffer * 10^(point-length). +// The buffer will be null-terminated. +// +// The input v must be > 0 and different from NaN, and Infinity. +// +// The output depends on the given mode: +// - SHORTEST: produce the least amount of digits for which the internal +// identity requirement is still satisfied. If the digits are printed +// (together with the correct exponent) then reading this number will give +// 'v' again. The buffer will choose the representation that is closest to +// 'v'. If there are two at the same distance, than the number is round up. +// In this mode the 'requested_digits' parameter is ignored. +// - FIXED: produces digits necessary to print a given number with +// 'requested_digits' digits after the decimal point. The produced digits +// might be too short in which case the caller has to fill the gaps with '0's. +// Example: toFixed(0.001, 5) is allowed to return buffer="1", point=-2. +// Halfway cases are rounded up. The call toFixed(0.15, 2) thus returns +// buffer="2", point=0. +// Note: the length of the returned buffer has no meaning wrt the significance +// of its digits. That is, just because it contains '0's does not mean that +// any other digit would not satisfy the internal identity requirement. +// - PRECISION: produces 'requested_digits' where the first digit is not '0'. +// Even though the length of produced digits usually equals +// 'requested_digits', the function is allowed to return fewer digits, in +// which case the caller has to fill the missing digits with '0's. +// Halfway cases are again rounded up. +// 'BignumDtoa' expects the given buffer to be big enough to hold all digits +// and a terminating null-character. +void BignumDtoa(double v, BignumDtoaMode mode, int requested_digits, + Vector buffer, int* length, int* point); + +} } // namespace v8::internal + +#endif // V8_BIGNUM_DTOA_H_ diff --git a/src/conversions.cc b/src/conversions.cc index a1ec089..c0dbf73 100644 --- a/src/conversions.cc +++ b/src/conversions.cc @@ -654,7 +654,7 @@ static double InternalStringToDouble(Iterator current, buffer[buffer_pos] = '\0'; double converted = Strtod(Vector(buffer, buffer_pos), exponent); - return sign? -converted: converted; + return sign ? -converted : converted; } @@ -711,11 +711,6 @@ double StringToDouble(Vector str, } -extern "C" char* dtoa(double d, int mode, int ndigits, - int* decpt, int* sign, char** rve); - -extern "C" void freedtoa(char* s); - const char* DoubleToCString(double v, Vector buffer) { StringBuilder builder(buffer.start(), buffer.length()); @@ -739,21 +734,13 @@ const char* DoubleToCString(double v, Vector buffer) { default: { int decimal_point; int sign; - char* decimal_rep; - bool used_gay_dtoa = false; const int kV8DtoaBufferCapacity = kBase10MaximalLength + 1; - char v8_dtoa_buffer[kV8DtoaBufferCapacity]; + char decimal_rep[kV8DtoaBufferCapacity]; int length; - if (DoubleToAscii(v, DTOA_SHORTEST, 0, - Vector(v8_dtoa_buffer, kV8DtoaBufferCapacity), - &sign, &length, &decimal_point)) { - decimal_rep = v8_dtoa_buffer; - } else { - decimal_rep = dtoa(v, 0, 0, &decimal_point, &sign, NULL); - used_gay_dtoa = true; - length = StrLength(decimal_rep); - } + DoubleToAscii(v, DTOA_SHORTEST, 0, + Vector(decimal_rep, kV8DtoaBufferCapacity), + &sign, &length, &decimal_point); if (sign) builder.AddCharacter('-'); @@ -787,8 +774,6 @@ const char* DoubleToCString(double v, Vector buffer) { if (exponent < 0) exponent = -exponent; builder.AddFormatted("%d", exponent); } - - if (used_gay_dtoa) freedtoa(decimal_rep); } } return builder.Finalize(); @@ -845,11 +830,9 @@ char* DoubleToFixedCString(double value, int f) { kMaxDigitsBeforePoint + kMaxDigitsAfterPoint + 1; char decimal_rep[kDecimalRepCapacity]; int decimal_rep_length; - bool status = DoubleToAscii(value, DTOA_FIXED, f, - Vector(decimal_rep, kDecimalRepCapacity), - &sign, &decimal_rep_length, &decimal_point); - USE(status); - ASSERT(status); + DoubleToAscii(value, DTOA_FIXED, f, + Vector(decimal_rep, kDecimalRepCapacity), + &sign, &decimal_rep_length, &decimal_point); // Create a representation that is padded with zeros if needed. int zero_prefix_length = 0; @@ -935,8 +918,6 @@ char* DoubleToExponentialCString(double value, int f) { // Find a sufficiently precise decimal representation of n. int decimal_point; int sign; - char* decimal_rep = NULL; - bool used_gay_dtoa = false; // f corresponds to the digits after the point. There is always one digit // before the point. The number of requested_digits equals hence f + 1. // And we have to add one character for the null-terminator. @@ -944,31 +925,18 @@ char* DoubleToExponentialCString(double value, int f) { // Make sure that the buffer is big enough, even if we fall back to the // shortest representation (which happens when f equals -1). ASSERT(kBase10MaximalLength <= kMaxDigitsAfterPoint + 1); - char v8_dtoa_buffer[kV8DtoaBufferCapacity]; + char decimal_rep[kV8DtoaBufferCapacity]; int decimal_rep_length; if (f == -1) { - if (DoubleToAscii(value, DTOA_SHORTEST, 0, - Vector(v8_dtoa_buffer, kV8DtoaBufferCapacity), - &sign, &decimal_rep_length, &decimal_point)) { - f = decimal_rep_length - 1; - decimal_rep = v8_dtoa_buffer; - } else { - decimal_rep = dtoa(value, 0, 0, &decimal_point, &sign, NULL); - decimal_rep_length = StrLength(decimal_rep); - f = decimal_rep_length - 1; - used_gay_dtoa = true; - } + DoubleToAscii(value, DTOA_SHORTEST, 0, + Vector(decimal_rep, kV8DtoaBufferCapacity), + &sign, &decimal_rep_length, &decimal_point); + f = decimal_rep_length - 1; } else { - if (DoubleToAscii(value, DTOA_PRECISION, f + 1, - Vector(v8_dtoa_buffer, kV8DtoaBufferCapacity), - &sign, &decimal_rep_length, &decimal_point)) { - decimal_rep = v8_dtoa_buffer; - } else { - decimal_rep = dtoa(value, 2, f + 1, &decimal_point, &sign, NULL); - decimal_rep_length = StrLength(decimal_rep); - used_gay_dtoa = true; - } + DoubleToAscii(value, DTOA_PRECISION, f + 1, + Vector(decimal_rep, kV8DtoaBufferCapacity), + &sign, &decimal_rep_length, &decimal_point); } ASSERT(decimal_rep_length > 0); ASSERT(decimal_rep_length <= f + 1); @@ -977,10 +945,6 @@ char* DoubleToExponentialCString(double value, int f) { char* result = CreateExponentialRepresentation(decimal_rep, exponent, negative, f+1); - if (used_gay_dtoa) { - freedtoa(decimal_rep); - } - return result; } @@ -1000,22 +964,14 @@ char* DoubleToPrecisionCString(double value, int p) { // Find a sufficiently precise decimal representation of n. int decimal_point; int sign; - char* decimal_rep = NULL; - bool used_gay_dtoa = false; // Add one for the terminating null character. const int kV8DtoaBufferCapacity = kMaximalDigits + 1; - char v8_dtoa_buffer[kV8DtoaBufferCapacity]; + char decimal_rep[kV8DtoaBufferCapacity]; int decimal_rep_length; - if (DoubleToAscii(value, DTOA_PRECISION, p, - Vector(v8_dtoa_buffer, kV8DtoaBufferCapacity), - &sign, &decimal_rep_length, &decimal_point)) { - decimal_rep = v8_dtoa_buffer; - } else { - decimal_rep = dtoa(value, 2, p, &decimal_point, &sign, NULL); - decimal_rep_length = StrLength(decimal_rep); - used_gay_dtoa = true; - } + DoubleToAscii(value, DTOA_PRECISION, p, + Vector(decimal_rep, kV8DtoaBufferCapacity), + &sign, &decimal_rep_length, &decimal_point); ASSERT(decimal_rep_length <= p); int exponent = decimal_point - 1; @@ -1059,9 +1015,6 @@ char* DoubleToPrecisionCString(double value, int p) { result = builder.Finalize(); } - if (used_gay_dtoa) { - freedtoa(decimal_rep); - } return result; } diff --git a/src/dtoa.cc b/src/dtoa.cc index f4141eb..b857a5d 100644 --- a/src/dtoa.cc +++ b/src/dtoa.cc @@ -30,6 +30,7 @@ #include "v8.h" #include "dtoa.h" +#include "bignum-dtoa.h" #include "double.h" #include "fast-dtoa.h" #include "fixed-dtoa.h" @@ -37,7 +38,19 @@ namespace v8 { namespace internal { -bool DoubleToAscii(double v, DtoaMode mode, int requested_digits, +static BignumDtoaMode DtoaToBignumDtoaMode(DtoaMode dtoa_mode) { + switch (dtoa_mode) { + case DTOA_SHORTEST: return BIGNUM_DTOA_SHORTEST; + case DTOA_FIXED: return BIGNUM_DTOA_FIXED; + case DTOA_PRECISION: return BIGNUM_DTOA_PRECISION; + default: + UNREACHABLE(); + return BIGNUM_DTOA_SHORTEST; // To silence compiler. + } +} + + +void DoubleToAscii(double v, DtoaMode mode, int requested_digits, Vector buffer, int* sign, int* length, int* point) { ASSERT(!Double(v).IsSpecial()); ASSERT(mode == DTOA_SHORTEST || requested_digits >= 0); @@ -54,25 +67,37 @@ bool DoubleToAscii(double v, DtoaMode mode, int requested_digits, buffer[1] = '\0'; *length = 1; *point = 1; - return true; + return; } if (mode == DTOA_PRECISION && requested_digits == 0) { buffer[0] = '\0'; *length = 0; - return true; + return; } + bool fast_worked; switch (mode) { case DTOA_SHORTEST: - return FastDtoa(v, FAST_DTOA_SHORTEST, 0, buffer, length, point); + fast_worked = FastDtoa(v, FAST_DTOA_SHORTEST, 0, buffer, length, point); + break; case DTOA_FIXED: - return FastFixedDtoa(v, requested_digits, buffer, length, point); + fast_worked = FastFixedDtoa(v, requested_digits, buffer, length, point); + break; case DTOA_PRECISION: - return FastDtoa(v, FAST_DTOA_PRECISION, requested_digits, - buffer, length, point); + fast_worked = FastDtoa(v, FAST_DTOA_PRECISION, requested_digits, + buffer, length, point); + break; + default: + UNREACHABLE(); + fast_worked = false; } - return false; + if (fast_worked) return; + + // If the fast dtoa didn't succeed use the slower bignum version. + BignumDtoaMode bignum_mode = DtoaToBignumDtoaMode(mode); + BignumDtoa(v, bignum_mode, requested_digits, buffer, length, point); + buffer[*length] = '\0'; } } } // namespace v8::internal diff --git a/src/dtoa.h b/src/dtoa.h index be0d545..b3e79af 100644 --- a/src/dtoa.h +++ b/src/dtoa.h @@ -32,13 +32,15 @@ namespace v8 { namespace internal { enum DtoaMode { - // 0.9999999999999999 becomes 0.1 + // Return the shortest correct representation. + // For example the output of 0.299999999999999988897 is (the less accurate but + // correct) 0.3. DTOA_SHORTEST, - // Fixed number of digits after the decimal point. + // Return a fixed number of digits after the decimal point. // For instance fixed(0.1, 4) becomes 0.1000 // If the input number is big, the output will be big. DTOA_FIXED, - // Fixed number of digits (independent of the decimal point). + // Return a fixed number of digits, no matter what the exponent is. DTOA_PRECISION }; @@ -72,8 +74,10 @@ static const int kBase10MaximalLength = 17; // which case the caller has to fill the missing digits with '0's. // Halfway cases are again rounded away from 0. // 'DoubleToAscii' expects the given buffer to be big enough to hold all digits -// and a terminating null-character. -bool DoubleToAscii(double v, DtoaMode mode, int requested_digits, +// and a terminating null-character. In SHORTEST-mode it expects a buffer of +// at least kBase10MaximalLength + 1. Otherwise, the size of the output is +// limited to requested_digits digits plus the null terminator. +void DoubleToAscii(double v, DtoaMode mode, int requested_digits, Vector buffer, int* sign, int* length, int* point); } } // namespace v8::internal diff --git a/test/cctest/SConscript b/test/cctest/SConscript index 620cd82..ba3466d 100644 --- a/test/cctest/SConscript +++ b/test/cctest/SConscript @@ -42,6 +42,7 @@ SOURCES = { 'test-api.cc', 'test-ast.cc', 'test-bignum.cc', + 'test-bignum-dtoa.cc', 'test-circular-queue.cc', 'test-compiler.cc', 'test-conversions.cc', @@ -51,6 +52,7 @@ SOURCES = { 'test-decls.cc', 'test-diy-fp.cc', 'test-double.cc', + 'test-dtoa.cc', 'test-fast-dtoa.cc', 'test-fixed-dtoa.cc', 'test-flags.cc', diff --git a/test/cctest/test-bignum-dtoa.cc b/test/cctest/test-bignum-dtoa.cc new file mode 100644 index 0000000..51a9057 --- /dev/null +++ b/test/cctest/test-bignum-dtoa.cc @@ -0,0 +1,315 @@ +// Copyright 2010 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include + +#include "v8.h" + +#include "bignum-dtoa.h" + +#include "cctest.h" +#include "double.h" +#include "gay-fixed.h" +#include "gay-precision.h" +#include "gay-shortest.h" +#include "platform.h" + +using namespace v8::internal; + + +// Removes trailing '0' digits. +// Can return the empty string if all digits are 0. +static void TrimRepresentation(Vector representation) { + int len = strlen(representation.start()); + int i; + for (i = len - 1; i >= 0; --i) { + if (representation[i] != '0') break; + } + representation[i + 1] = '\0'; +} + + +static const int kBufferSize = 100; + + +TEST(BignumDtoaVariousDoubles) { + char buffer_container[kBufferSize]; + Vector buffer(buffer_container, kBufferSize); + int length; + int point; + + BignumDtoa(1.0, BIGNUM_DTOA_SHORTEST, 0, buffer, &length, &point); + CHECK_EQ("1", buffer.start()); + CHECK_EQ(1, point); + + BignumDtoa(1.0, BIGNUM_DTOA_FIXED, 3, buffer, &length, &point); + CHECK_GE(3, length - point); + TrimRepresentation(buffer); + CHECK_EQ("1", buffer.start()); + CHECK_EQ(1, point); + + BignumDtoa(1.0, BIGNUM_DTOA_PRECISION, 3, buffer, &length, &point); + CHECK_GE(3, length); + TrimRepresentation(buffer); + CHECK_EQ("1", buffer.start()); + CHECK_EQ(1, point); + + BignumDtoa(1.5, BIGNUM_DTOA_SHORTEST, 0, buffer, &length, &point); + CHECK_EQ("15", buffer.start()); + CHECK_EQ(1, point); + + BignumDtoa(1.5, BIGNUM_DTOA_FIXED, 10, buffer, &length, &point); + CHECK_GE(10, length - point); + TrimRepresentation(buffer); + CHECK_EQ("15", buffer.start()); + CHECK_EQ(1, point); + + BignumDtoa(1.5, BIGNUM_DTOA_PRECISION, 10, buffer, &length, &point); + CHECK_GE(10, length); + TrimRepresentation(buffer); + CHECK_EQ("15", buffer.start()); + CHECK_EQ(1, point); + + double min_double = 5e-324; + BignumDtoa(min_double, BIGNUM_DTOA_SHORTEST, 0, buffer, &length, &point); + CHECK_EQ("5", buffer.start()); + CHECK_EQ(-323, point); + + BignumDtoa(min_double, BIGNUM_DTOA_FIXED, 5, buffer, &length, &point); + CHECK_GE(5, length - point); + TrimRepresentation(buffer); + CHECK_EQ("", buffer.start()); + + BignumDtoa(min_double, BIGNUM_DTOA_PRECISION, 5, buffer, &length, &point); + CHECK_GE(5, length); + TrimRepresentation(buffer); + CHECK_EQ("49407", buffer.start()); + CHECK_EQ(-323, point); + + double max_double = 1.7976931348623157e308; + BignumDtoa(max_double, BIGNUM_DTOA_SHORTEST, 0, buffer, &length, &point); + CHECK_EQ("17976931348623157", buffer.start()); + CHECK_EQ(309, point); + + BignumDtoa(max_double, BIGNUM_DTOA_PRECISION, 7, buffer, &length, &point); + CHECK_GE(7, length); + TrimRepresentation(buffer); + CHECK_EQ("1797693", buffer.start()); + CHECK_EQ(309, point); + + BignumDtoa(4294967272.0, BIGNUM_DTOA_SHORTEST, 0, buffer, &length, &point); + CHECK_EQ("4294967272", buffer.start()); + CHECK_EQ(10, point); + + BignumDtoa(4294967272.0, BIGNUM_DTOA_FIXED, 5, buffer, &length, &point); + CHECK_EQ("429496727200000", buffer.start()); + CHECK_EQ(10, point); + + + BignumDtoa(4294967272.0, BIGNUM_DTOA_PRECISION, 14, buffer, &length, &point); + CHECK_GE(14, length); + TrimRepresentation(buffer); + CHECK_EQ("4294967272", buffer.start()); + CHECK_EQ(10, point); + + BignumDtoa(4.1855804968213567e298, BIGNUM_DTOA_SHORTEST, 0, + buffer, &length, &point); + CHECK_EQ("4185580496821357", buffer.start()); + CHECK_EQ(299, point); + + BignumDtoa(4.1855804968213567e298, BIGNUM_DTOA_PRECISION, 20, + buffer, &length, &point); + CHECK_GE(20, length); + TrimRepresentation(buffer); + CHECK_EQ("41855804968213567225", buffer.start()); + CHECK_EQ(299, point); + + BignumDtoa(5.5626846462680035e-309, BIGNUM_DTOA_SHORTEST, 0, + buffer, &length, &point); + CHECK_EQ("5562684646268003", buffer.start()); + CHECK_EQ(-308, point); + + BignumDtoa(5.5626846462680035e-309, BIGNUM_DTOA_PRECISION, 1, + buffer, &length, &point); + CHECK_GE(1, length); + TrimRepresentation(buffer); + CHECK_EQ("6", buffer.start()); + CHECK_EQ(-308, point); + + BignumDtoa(2147483648.0, BIGNUM_DTOA_SHORTEST, 0, + buffer, &length, &point); + CHECK_EQ("2147483648", buffer.start()); + CHECK_EQ(10, point); + + + BignumDtoa(2147483648.0, BIGNUM_DTOA_FIXED, 2, + buffer, &length, &point); + CHECK_GE(2, length - point); + TrimRepresentation(buffer); + CHECK_EQ("2147483648", buffer.start()); + CHECK_EQ(10, point); + + BignumDtoa(2147483648.0, BIGNUM_DTOA_PRECISION, 5, + buffer, &length, &point); + CHECK_GE(5, length); + TrimRepresentation(buffer); + CHECK_EQ("21475", buffer.start()); + CHECK_EQ(10, point); + + BignumDtoa(3.5844466002796428e+298, BIGNUM_DTOA_SHORTEST, 0, + buffer, &length, &point); + CHECK_EQ("35844466002796428", buffer.start()); + CHECK_EQ(299, point); + + BignumDtoa(3.5844466002796428e+298, BIGNUM_DTOA_PRECISION, 10, + buffer, &length, &point); + CHECK_GE(10, length); + TrimRepresentation(buffer); + CHECK_EQ("35844466", buffer.start()); + CHECK_EQ(299, point); + + uint64_t smallest_normal64 = V8_2PART_UINT64_C(0x00100000, 00000000); + double v = Double(smallest_normal64).value(); + BignumDtoa(v, BIGNUM_DTOA_SHORTEST, 0, buffer, &length, &point); + CHECK_EQ("22250738585072014", buffer.start()); + CHECK_EQ(-307, point); + + BignumDtoa(v, BIGNUM_DTOA_PRECISION, 20, buffer, &length, &point); + CHECK_GE(20, length); + TrimRepresentation(buffer); + CHECK_EQ("22250738585072013831", buffer.start()); + CHECK_EQ(-307, point); + + uint64_t largest_denormal64 = V8_2PART_UINT64_C(0x000FFFFF, FFFFFFFF); + v = Double(largest_denormal64).value(); + BignumDtoa(v, BIGNUM_DTOA_SHORTEST, 0, buffer, &length, &point); + CHECK_EQ("2225073858507201", buffer.start()); + CHECK_EQ(-307, point); + + BignumDtoa(v, BIGNUM_DTOA_PRECISION, 20, buffer, &length, &point); + CHECK_GE(20, length); + TrimRepresentation(buffer); + CHECK_EQ("2225073858507200889", buffer.start()); + CHECK_EQ(-307, point); + + BignumDtoa(4128420500802942e-24, BIGNUM_DTOA_SHORTEST, 0, + buffer, &length, &point); + CHECK_EQ("4128420500802942", buffer.start()); + CHECK_EQ(-8, point); + + v = 3.9292015898194142585311918e-10; + BignumDtoa(v, BIGNUM_DTOA_SHORTEST, 0, buffer, &length, &point); + CHECK_EQ("39292015898194143", buffer.start()); + + v = 4194304.0; + BignumDtoa(v, BIGNUM_DTOA_FIXED, 5, buffer, &length, &point); + CHECK_GE(5, length - point); + TrimRepresentation(buffer); + CHECK_EQ("4194304", buffer.start()); + + v = 3.3161339052167390562200598e-237; + BignumDtoa(v, BIGNUM_DTOA_PRECISION, 19, buffer, &length, &point); + CHECK_GE(19, length); + TrimRepresentation(buffer); + CHECK_EQ("3316133905216739056", buffer.start()); + CHECK_EQ(-236, point); + + v = 7.9885183916008099497815232e+191; + BignumDtoa(v, BIGNUM_DTOA_PRECISION, 4, buffer, &length, &point); + CHECK_GE(4, length); + TrimRepresentation(buffer); + CHECK_EQ("7989", buffer.start()); + CHECK_EQ(192, point); + + v = 1.0000000000000012800000000e+17; + BignumDtoa(v, BIGNUM_DTOA_FIXED, 1, buffer, &length, &point); + CHECK_GE(1, length - point); + TrimRepresentation(buffer); + CHECK_EQ("100000000000000128", buffer.start()); + CHECK_EQ(18, point); +} + + +TEST(BignumDtoaGayShortest) { + char buffer_container[kBufferSize]; + Vector buffer(buffer_container, kBufferSize); + int length; + int point; + + Vector precomputed = + PrecomputedShortestRepresentations(); + for (int i = 0; i < precomputed.length(); ++i) { + const PrecomputedShortest current_test = precomputed[i]; + double v = current_test.v; + BignumDtoa(v, BIGNUM_DTOA_SHORTEST, 0, buffer, &length, &point); + CHECK_EQ(current_test.decimal_point, point); + CHECK_EQ(current_test.representation, buffer.start()); + } +} + + +TEST(BignumDtoaGayFixed) { + char buffer_container[kBufferSize]; + Vector buffer(buffer_container, kBufferSize); + int length; + int point; + + Vector precomputed = + PrecomputedFixedRepresentations(); + for (int i = 0; i < precomputed.length(); ++i) { + const PrecomputedFixed current_test = precomputed[i]; + double v = current_test.v; + int number_digits = current_test.number_digits; + BignumDtoa(v, BIGNUM_DTOA_FIXED, number_digits, buffer, &length, &point); + CHECK_EQ(current_test.decimal_point, point); + CHECK_GE(number_digits, length - point); + TrimRepresentation(buffer); + CHECK_EQ(current_test.representation, buffer.start()); + } +} + + +TEST(BignumDtoaGayPrecision) { + char buffer_container[kBufferSize]; + Vector buffer(buffer_container, kBufferSize); + int length; + int point; + + Vector precomputed = + PrecomputedPrecisionRepresentations(); + for (int i = 0; i < precomputed.length(); ++i) { + const PrecomputedPrecision current_test = precomputed[i]; + double v = current_test.v; + int number_digits = current_test.number_digits; + BignumDtoa(v, BIGNUM_DTOA_PRECISION, number_digits, + buffer, &length, &point); + CHECK_EQ(current_test.decimal_point, point); + CHECK_GE(number_digits, length); + TrimRepresentation(buffer); + CHECK_EQ(current_test.representation, buffer.start()); + } +} diff --git a/test/cctest/test-dtoa.cc b/test/cctest/test-dtoa.cc new file mode 100644 index 0000000..ff0b660 --- /dev/null +++ b/test/cctest/test-dtoa.cc @@ -0,0 +1,331 @@ +// Copyright 2010 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include + +#include "v8.h" + +#include "dtoa.h" + +#include "cctest.h" +#include "double.h" +#include "gay-fixed.h" +#include "gay-precision.h" +#include "gay-shortest.h" +#include "platform.h" + + +using namespace v8::internal; + + +// Removes trailing '0' digits. +static void TrimRepresentation(Vector representation) { + int len = strlen(representation.start()); + int i; + for (i = len - 1; i >= 0; --i) { + if (representation[i] != '0') break; + } + representation[i + 1] = '\0'; +} + + +static const int kBufferSize = 100; + + +TEST(DtoaVariousDoubles) { + char buffer_container[kBufferSize]; + Vector buffer(buffer_container, kBufferSize); + int length; + int point; + int sign; + + DoubleToAscii(0.0, DTOA_SHORTEST, 0, buffer, &sign, &length, &point); + CHECK_EQ("0", buffer.start()); + CHECK_EQ(1, point); + + DoubleToAscii(0.0, DTOA_FIXED, 2, buffer, &sign, &length, &point); + CHECK_EQ(1, length); + CHECK_EQ("0", buffer.start()); + CHECK_EQ(1, point); + + DoubleToAscii(0.0, DTOA_PRECISION, 3, buffer, &sign, &length, &point); + CHECK_EQ(1, length); + CHECK_EQ("0", buffer.start()); + CHECK_EQ(1, point); + + DoubleToAscii(1.0, DTOA_SHORTEST, 0, buffer, &sign, &length, &point); + CHECK_EQ("1", buffer.start()); + CHECK_EQ(1, point); + + DoubleToAscii(1.0, DTOA_FIXED, 3, buffer, &sign, &length, &point); + CHECK_GE(3, length - point); + TrimRepresentation(buffer); + CHECK_EQ("1", buffer.start()); + CHECK_EQ(1, point); + + DoubleToAscii(1.0, DTOA_PRECISION, 3, buffer, &sign, &length, &point); + CHECK_GE(3, length); + TrimRepresentation(buffer); + CHECK_EQ("1", buffer.start()); + CHECK_EQ(1, point); + + DoubleToAscii(1.5, DTOA_SHORTEST, 0, buffer, &sign, &length, &point); + CHECK_EQ("15", buffer.start()); + CHECK_EQ(1, point); + + DoubleToAscii(1.5, DTOA_FIXED, 10, buffer, &sign, &length, &point); + CHECK_GE(10, length - point); + TrimRepresentation(buffer); + CHECK_EQ("15", buffer.start()); + CHECK_EQ(1, point); + + DoubleToAscii(1.5, DTOA_PRECISION, 10, buffer, &sign, &length, &point); + CHECK_GE(10, length); + TrimRepresentation(buffer); + CHECK_EQ("15", buffer.start()); + CHECK_EQ(1, point); + + double min_double = 5e-324; + DoubleToAscii(min_double, DTOA_SHORTEST, 0, buffer, &sign, &length, &point); + CHECK_EQ("5", buffer.start()); + CHECK_EQ(-323, point); + + DoubleToAscii(min_double, DTOA_FIXED, 5, buffer, &sign, &length, &point); + CHECK_GE(5, length - point); + TrimRepresentation(buffer); + CHECK_EQ("", buffer.start()); + CHECK_GE(-5, point); + + DoubleToAscii(min_double, DTOA_PRECISION, 5, buffer, &sign, &length, &point); + CHECK_GE(5, length); + TrimRepresentation(buffer); + CHECK_EQ("49407", buffer.start()); + CHECK_EQ(-323, point); + + double max_double = 1.7976931348623157e308; + DoubleToAscii(max_double, DTOA_SHORTEST, 0, buffer, &sign, &length, &point); + CHECK_EQ("17976931348623157", buffer.start()); + CHECK_EQ(309, point); + + DoubleToAscii(max_double, DTOA_PRECISION, 7, buffer, &sign, &length, &point); + CHECK_GE(7, length); + TrimRepresentation(buffer); + CHECK_EQ("1797693", buffer.start()); + CHECK_EQ(309, point); + + DoubleToAscii(4294967272.0, DTOA_SHORTEST, 0, buffer, &sign, &length, &point); + CHECK_EQ("4294967272", buffer.start()); + CHECK_EQ(10, point); + + DoubleToAscii(4294967272.0, DTOA_FIXED, 5, buffer, &sign, &length, &point); + CHECK_GE(5, length - point); + TrimRepresentation(buffer); + CHECK_EQ("4294967272", buffer.start()); + CHECK_EQ(10, point); + + + DoubleToAscii(4294967272.0, DTOA_PRECISION, 14, + buffer, &sign, &length, &point); + CHECK_GE(14, length); + TrimRepresentation(buffer); + CHECK_EQ("4294967272", buffer.start()); + CHECK_EQ(10, point); + + DoubleToAscii(4.1855804968213567e298, DTOA_SHORTEST, 0, + buffer, &sign, &length, &point); + CHECK_EQ("4185580496821357", buffer.start()); + CHECK_EQ(299, point); + + DoubleToAscii(4.1855804968213567e298, DTOA_PRECISION, 20, + buffer, &sign, &length, &point); + CHECK_GE(20, length); + TrimRepresentation(buffer); + CHECK_EQ("41855804968213567225", buffer.start()); + CHECK_EQ(299, point); + + DoubleToAscii(5.5626846462680035e-309, DTOA_SHORTEST, 0, + buffer, &sign, &length, &point); + CHECK_EQ("5562684646268003", buffer.start()); + CHECK_EQ(-308, point); + + DoubleToAscii(5.5626846462680035e-309, DTOA_PRECISION, 1, + buffer, &sign, &length, &point); + CHECK_GE(1, length); + TrimRepresentation(buffer); + CHECK_EQ("6", buffer.start()); + CHECK_EQ(-308, point); + + DoubleToAscii(-2147483648.0, DTOA_SHORTEST, 0, + buffer, &sign, &length, &point); + CHECK_EQ(1, sign); + CHECK_EQ("2147483648", buffer.start()); + CHECK_EQ(10, point); + + + DoubleToAscii(-2147483648.0, DTOA_FIXED, 2, buffer, &sign, &length, &point); + CHECK_GE(2, length - point); + TrimRepresentation(buffer); + CHECK_EQ(1, sign); + CHECK_EQ("2147483648", buffer.start()); + CHECK_EQ(10, point); + + DoubleToAscii(-2147483648.0, DTOA_PRECISION, 5, + buffer, &sign, &length, &point); + CHECK_GE(5, length); + TrimRepresentation(buffer); + CHECK_EQ(1, sign); + CHECK_EQ("21475", buffer.start()); + CHECK_EQ(10, point); + + DoubleToAscii(-3.5844466002796428e+298, DTOA_SHORTEST, 0, + buffer, &sign, &length, &point); + CHECK_EQ(1, sign); + CHECK_EQ("35844466002796428", buffer.start()); + CHECK_EQ(299, point); + + DoubleToAscii(-3.5844466002796428e+298, DTOA_PRECISION, 10, + buffer, &sign, &length, &point); + CHECK_EQ(1, sign); + CHECK_GE(10, length); + TrimRepresentation(buffer); + CHECK_EQ("35844466", buffer.start()); + CHECK_EQ(299, point); + + uint64_t smallest_normal64 = V8_2PART_UINT64_C(0x00100000, 00000000); + double v = Double(smallest_normal64).value(); + DoubleToAscii(v, DTOA_SHORTEST, 0, buffer, &sign, &length, &point); + CHECK_EQ("22250738585072014", buffer.start()); + CHECK_EQ(-307, point); + + DoubleToAscii(v, DTOA_PRECISION, 20, buffer, &sign, &length, &point); + CHECK_GE(20, length); + TrimRepresentation(buffer); + CHECK_EQ("22250738585072013831", buffer.start()); + CHECK_EQ(-307, point); + + uint64_t largest_denormal64 = V8_2PART_UINT64_C(0x000FFFFF, FFFFFFFF); + v = Double(largest_denormal64).value(); + DoubleToAscii(v, DTOA_SHORTEST, 0, buffer, &sign, &length, &point); + CHECK_EQ("2225073858507201", buffer.start()); + CHECK_EQ(-307, point); + + DoubleToAscii(v, DTOA_PRECISION, 20, buffer, &sign, &length, &point); + CHECK_GE(20, length); + TrimRepresentation(buffer); + CHECK_EQ("2225073858507200889", buffer.start()); + CHECK_EQ(-307, point); + + DoubleToAscii(4128420500802942e-24, DTOA_SHORTEST, 0, + buffer, &sign, &length, &point); + CHECK_EQ(0, sign); + CHECK_EQ("4128420500802942", buffer.start()); + CHECK_EQ(-8, point); + + v = -3.9292015898194142585311918e-10; + DoubleToAscii(v, DTOA_SHORTEST, 0, buffer, &sign, &length, &point); + CHECK_EQ("39292015898194143", buffer.start()); + + v = 4194304.0; + DoubleToAscii(v, DTOA_FIXED, 5, buffer, &sign, &length, &point); + CHECK_GE(5, length - point); + TrimRepresentation(buffer); + CHECK_EQ("4194304", buffer.start()); + + v = 3.3161339052167390562200598e-237; + DoubleToAscii(v, DTOA_PRECISION, 19, buffer, &sign, &length, &point); + CHECK_GE(19, length); + TrimRepresentation(buffer); + CHECK_EQ("3316133905216739056", buffer.start()); + CHECK_EQ(-236, point); +} + + +TEST(DtoaGayShortest) { + char buffer_container[kBufferSize]; + Vector buffer(buffer_container, kBufferSize); + int sign; + int length; + int point; + + Vector precomputed = + PrecomputedShortestRepresentations(); + for (int i = 0; i < precomputed.length(); ++i) { + const PrecomputedShortest current_test = precomputed[i]; + double v = current_test.v; + DoubleToAscii(v, DTOA_SHORTEST, 0, buffer, &sign, &length, &point); + CHECK_EQ(0, sign); // All precomputed numbers are positive. + CHECK_EQ(current_test.decimal_point, point); + CHECK_EQ(current_test.representation, buffer.start()); + } +} + + +TEST(DtoaGayFixed) { + char buffer_container[kBufferSize]; + Vector buffer(buffer_container, kBufferSize); + int sign; + int length; + int point; + + Vector precomputed = + PrecomputedFixedRepresentations(); + for (int i = 0; i < precomputed.length(); ++i) { + const PrecomputedFixed current_test = precomputed[i]; + double v = current_test.v; + int number_digits = current_test.number_digits; + DoubleToAscii(v, DTOA_FIXED, number_digits, buffer, &sign, &length, &point); + CHECK_EQ(0, sign); // All precomputed numbers are positive. + CHECK_EQ(current_test.decimal_point, point); + CHECK_GE(number_digits, length - point); + TrimRepresentation(buffer); + CHECK_EQ(current_test.representation, buffer.start()); + } +} + + +TEST(DtoaGayPrecision) { + char buffer_container[kBufferSize]; + Vector buffer(buffer_container, kBufferSize); + int sign; + int length; + int point; + + Vector precomputed = + PrecomputedPrecisionRepresentations(); + for (int i = 0; i < precomputed.length(); ++i) { + const PrecomputedPrecision current_test = precomputed[i]; + double v = current_test.v; + int number_digits = current_test.number_digits; + DoubleToAscii(v, DTOA_PRECISION, number_digits, + buffer, &sign, &length, &point); + CHECK_EQ(0, sign); // All precomputed numbers are positive. + CHECK_EQ(current_test.decimal_point, point); + CHECK_GE(number_digits, length); + TrimRepresentation(buffer); + CHECK_EQ(current_test.representation, buffer.start()); + } +} diff --git a/tools/gyp/v8.gyp b/tools/gyp/v8.gyp index f1777df..30b15a9 100644 --- a/tools/gyp/v8.gyp +++ b/tools/gyp/v8.gyp @@ -282,6 +282,8 @@ '../../src/ast.h', '../../src/bignum.cc', '../../src/bignum.h', + '../../src/bignum-dtoa.cc', + '../../src/bignum-dtoa.h', '../../src/bootstrapper.cc', '../../src/bootstrapper.h', '../../src/builtins.cc',