Index: src/conversions.cc |
=================================================================== |
--- src/conversions.cc (revision 4304) |
+++ src/conversions.cc (working copy) |
@@ -274,73 +274,111 @@ |
} |
-template <class Iterator, class EndMark> |
-static double InternalHexadecimalStringToDouble(Iterator current, |
- EndMark end, |
- char* buffer, |
- bool allow_trailing_junk) { |
- ASSERT(current != end); |
+static bool isDigit(int x, int radix) { |
+ return (x >= '0' && x <= '9' && x < '0' + radix) |
+ || (radix > 10 && x >= 'a' && x < 'a' + radix - 10) |
+ || (radix > 10 && x >= 'A' && x < 'A' + radix - 10); |
+} |
- const int max_hex_significant_digits = 52 / 4 + 2; |
- // We reuse the buffer of InternalStringToDouble. Since hexadecimal |
- // numbers may have much less digits than decimal the buffer won't overflow. |
- ASSERT(max_hex_significant_digits < kMaxSignificantDigits); |
- int significant_digits = 0; |
- int insignificant_digits = 0; |
- bool leading_zero = false; |
- // A double has a 53bit significand (once the hidden bit has been added). |
- // Halfway cases thus have at most 54bits. Therefore 54/4 + 1 digits are |
- // sufficient to represent halfway cases. By adding another digit we can keep |
- // track of dropped digits. |
- int buffer_pos = 0; |
- bool nonzero_digit_dropped = false; |
+// Parsing integers with radix 2, 4, 8, 16, 32. Assumes current != end. |
+template <int radix_log_2, class Iterator, class EndMark> |
+ static double InternalStringToIntDouble(Iterator current, |
+ EndMark end, |
+ bool sign, |
+ bool allow_trailing_junk) { |
+ ASSERT(current != end); |
// Skip leading 0s. |
while (*current == '0') { |
- leading_zero = true; |
++current; |
- if (current == end) return 0; |
+ if (current == end) return sign ? -0.0 : 0.0; |
} |
- int begin_pos = buffer_pos; |
- while ((*current >= '0' && *current <= '9') |
- || (*current >= 'a' && *current <= 'f') |
- || (*current >= 'A' && *current <= 'F')) { |
- if (significant_digits <= max_hex_significant_digits) { |
- buffer[buffer_pos++] = static_cast<char>(*current); |
- significant_digits++; |
+ int64_t number = 0; |
+ int exponent = 0; |
+ const int radix = (1 << radix_log_2); |
+ |
+ do { |
+ int digit; |
+ if (*current >= '0' && *current <= '9' && *current < '0' + radix) { |
+ digit = static_cast<char>(*current) - '0'; |
+ } else if (radix > 10 && *current >= 'a' && *current < 'a' + radix - 10) { |
+ digit = static_cast<char>(*current) - 'a' + 10; |
+ } else if (radix > 10 && *current >= 'A' && *current < 'A' + radix - 10) { |
+ digit = static_cast<char>(*current) - 'A' + 10; |
} else { |
- insignificant_digits++; |
- nonzero_digit_dropped = nonzero_digit_dropped || *current != '0'; |
+ if (allow_trailing_junk || !AdvanceToNonspace(¤t, end)) { |
+ break; |
+ } else { |
+ return JUNK_STRING_VALUE; |
+ } |
} |
- ++current; |
- if (current == end) break; |
- } |
- if (!allow_trailing_junk && AdvanceToNonspace(¤t, end)) { |
- return JUNK_STRING_VALUE; |
- } |
+ number = number * radix + digit; |
+ int overflow = number >> 53; |
+ if (overflow != 0) { |
+ // Overflow occurred. Need to determine which direction to round the |
+ // result. |
+ int overflow_bits_count = 1; |
+ while (overflow > 1) { |
+ overflow_bits_count++; |
+ overflow >>= 1; |
+ } |
- if (significant_digits == 0) { |
- return leading_zero ? 0 : JUNK_STRING_VALUE; |
- } |
+ int dropped_bits_mask = ((1 << overflow_bits_count) - 1); |
+ int dropped_bits = number & dropped_bits_mask; |
+ number >>= overflow_bits_count; |
+ exponent = overflow_bits_count; |
- if (nonzero_digit_dropped) { |
- ASSERT(insignificant_digits > 0); |
- insignificant_digits--; |
- buffer[buffer_pos++] = '1'; |
- } |
+ bool zero_tail = true; |
+ while (true) { |
+ ++current; |
+ if (current == end || !isDigit(*current, radix)) break; |
+ zero_tail = zero_tail && *current == '0'; |
+ exponent += radix_log_2; |
+ } |
- buffer[buffer_pos] = '\0'; |
+ if (!allow_trailing_junk && AdvanceToNonspace(¤t, end)) { |
+ return JUNK_STRING_VALUE; |
+ } |
- double result; |
- StringToInt(buffer, begin_pos, 16, &result); |
- if (insignificant_digits > 0) { |
- // Multiplying by a power of 2 doesn't cause a loss of precision. |
- result *= pow(16.0, insignificant_digits); |
+ int middle_value = (1 << (overflow_bits_count - 1)); |
+ if (dropped_bits > middle_value) { |
+ number++; // Rounding up. |
+ } else if (dropped_bits == middle_value) { |
+ // Rounding to even to consistency with decimals: half-way case rounds |
+ // up if significant part is odd and down otherwise. |
+ if ((number & 1) != 0 || !zero_tail) { |
+ number++; // Rounding up. |
+ } |
+ } |
+ |
+ // Rounding up may cause overflow. |
+ if ((number & ((int64_t)1 << 53)) != 0) { |
+ exponent++; |
+ number >>= 1; |
+ } |
+ break; |
+ } |
+ ++current; |
+ } while (current != end); |
+ |
+ ASSERT(number < ((int64_t)1 << 53)); |
+ ASSERT(static_cast<int64_t>(static_cast<double>(number)) == number); |
+ |
+ if (exponent == 0) { |
+ if (sign) { |
+ if (number == 0) return -0.0; |
+ number = -number; |
+ } |
+ return static_cast<double>(number); |
} |
- return result; |
+ |
+ ASSERT(number != 0); |
+ // The double could be constructed faster from number (mantissa), exponent |
+ // and sign. Assuming it's a rare case more simple code is used. |
+ return static_cast<double>(sign ? -number : number) * pow(2.0, exponent); |
} |
@@ -413,16 +451,16 @@ |
leading_zero = true; |
- // It could be hexadecimal value. |
+// It could be hexadecimal value. |
if ((flags & ALLOW_HEX) && (*current == 'x' || *current == 'X')) { |
++current; |
if (current == end) return JUNK_STRING_VALUE; // "0x". |
- double result = InternalHexadecimalStringToDouble(current, |
- end, |
- buffer + buffer_pos, |
- allow_trailing_junk); |
- return (buffer_pos > 0 && buffer[0] == '-') ? -result : result; |
+ bool sign = (buffer_pos > 0 && buffer[0] == '-'); |
+ return InternalStringToIntDouble<4>(current, |
+ end, |
+ sign, |
+ allow_trailing_junk); |
} |
// Ignore leading zeros in the integer part. |
@@ -560,22 +598,13 @@ |
exponent += insignificant_digits; |
if (octal) { |
- buffer[buffer_pos] = '\0'; |
- // ALLOW_OCTALS is set and there is no '8' or '9' in insignificant |
- // digits. Check significant digits now. |
- char sign = '+'; |
- const char* s = buffer; |
- if (*s == '-' || *s == '+') sign = *s++; |
+ bool sign = buffer[0] == '-'; |
+ int start_pos = (sign ? 1 : 0); |
- double result; |
- s += StringToInt(s, 0, 8, &result); |
- if (!allow_trailing_junk && *s != '\0') return JUNK_STRING_VALUE; |
- |
- if (sign == '-') result = -result; |
- if (insignificant_digits > 0) { |
- result *= pow(8.0, insignificant_digits); |
- } |
- return result; |
+ return InternalStringToIntDouble<3>(buffer + start_pos, |
+ buffer + buffer_pos, |
+ sign, |
+ allow_trailing_junk); |
} |
if (nonzero_digit_dropped) { |