Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1271)

Side by Side Diff: src/conversions.cc

Issue 1374005: Percise rounding parsing octal and hexadecimal strings.... (Closed) Base URL: http://v8.googlecode.com/svn/branches/bleeding_edge/
Patch Set: '' Created 10 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | test/mjsunit/number-tostring.js » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2006-2008 the V8 project authors. All rights reserved. 1 // Copyright 2006-2008 the V8 project authors. All rights reserved.
2 // Redistribution and use in source and binary forms, with or without 2 // Redistribution and use in source and binary forms, with or without
3 // modification, are permitted provided that the following conditions are 3 // modification, are permitted provided that the following conditions are
4 // met: 4 // met:
5 // 5 //
6 // * Redistributions of source code must retain the above copyright 6 // * Redistributions of source code must retain the above copyright
7 // notice, this list of conditions and the following disclaimer. 7 // notice, this list of conditions and the following disclaimer.
8 // * Redistributions in binary form must reproduce the above 8 // * Redistributions in binary form must reproduce the above
9 // copyright notice, this list of conditions and the following 9 // copyright notice, this list of conditions and the following
10 // disclaimer in the documentation and/or other materials provided 10 // disclaimer in the documentation and/or other materials provided
(...skipping 256 matching lines...) Expand 10 before | Expand all | Expand 10 after
267 template <class Iterator, class EndMark> 267 template <class Iterator, class EndMark>
268 static inline bool AdvanceToNonspace(Iterator* current, EndMark end) { 268 static inline bool AdvanceToNonspace(Iterator* current, EndMark end) {
269 while (*current != end) { 269 while (*current != end) {
270 if (!Scanner::kIsWhiteSpace.get(**current)) return true; 270 if (!Scanner::kIsWhiteSpace.get(**current)) return true;
271 ++*current; 271 ++*current;
272 } 272 }
273 return false; 273 return false;
274 } 274 }
275 275
276 276
277 template <class Iterator, class EndMark> 277 static bool isDigit(int x, int radix) {
278 static double InternalHexadecimalStringToDouble(Iterator current, 278 return (x >= '0' && x <= '9' && x < '0' + radix)
279 EndMark end, 279 || (radix > 10 && x >= 'a' && x < 'a' + radix - 10)
280 char* buffer, 280 || (radix > 10 && x >= 'A' && x < 'A' + radix - 10);
281 bool allow_trailing_junk) { 281 }
282
283
284 // Parsing integers with radix 2, 4, 8, 16, 32. Assumes current != end.
285 template <int radix_log_2, class Iterator, class EndMark>
286 static double InternalStringToIntDouble(Iterator current,
287 EndMark end,
288 bool sign,
289 bool allow_trailing_junk) {
282 ASSERT(current != end); 290 ASSERT(current != end);
283 291
284 const int max_hex_significant_digits = 52 / 4 + 2;
285 // We reuse the buffer of InternalStringToDouble. Since hexadecimal
286 // numbers may have much less digits than decimal the buffer won't overflow.
287 ASSERT(max_hex_significant_digits < kMaxSignificantDigits);
288
289 int significant_digits = 0;
290 int insignificant_digits = 0;
291 bool leading_zero = false;
292 // A double has a 53bit significand (once the hidden bit has been added).
293 // Halfway cases thus have at most 54bits. Therefore 54/4 + 1 digits are
294 // sufficient to represent halfway cases. By adding another digit we can keep
295 // track of dropped digits.
296 int buffer_pos = 0;
297 bool nonzero_digit_dropped = false;
298
299 // Skip leading 0s. 292 // Skip leading 0s.
300 while (*current == '0') { 293 while (*current == '0') {
301 leading_zero = true;
302 ++current; 294 ++current;
303 if (current == end) return 0; 295 if (current == end) return sign ? -0.0 : 0.0;
304 } 296 }
305 297
306 int begin_pos = buffer_pos; 298 int64_t number = 0;
307 while ((*current >= '0' && *current <= '9') 299 int exponent = 0;
308 || (*current >= 'a' && *current <= 'f') 300 const int radix = (1 << radix_log_2);
309 || (*current >= 'A' && *current <= 'F')) { 301
310 if (significant_digits <= max_hex_significant_digits) { 302 do {
311 buffer[buffer_pos++] = static_cast<char>(*current); 303 int digit;
312 significant_digits++; 304 if (*current >= '0' && *current <= '9' && *current < '0' + radix) {
305 digit = static_cast<char>(*current) - '0';
306 } else if (radix > 10 && *current >= 'a' && *current < 'a' + radix - 10) {
307 digit = static_cast<char>(*current) - 'a' + 10;
308 } else if (radix > 10 && *current >= 'A' && *current < 'A' + radix - 10) {
309 digit = static_cast<char>(*current) - 'A' + 10;
313 } else { 310 } else {
314 insignificant_digits++; 311 if (allow_trailing_junk || !AdvanceToNonspace(&current, end)) {
315 nonzero_digit_dropped = nonzero_digit_dropped || *current != '0'; 312 break;
313 } else {
314 return JUNK_STRING_VALUE;
315 }
316 }
317
318 number = number * radix + digit;
319 int overflow = number >> 53;
320 if (overflow != 0) {
321 // Overflow occurred. Need to determine which direction to round the
322 // result.
323 int overflow_bits_count = 1;
324 while (overflow > 1) {
325 overflow_bits_count++;
326 overflow >>= 1;
327 }
328
329 int dropped_bits_mask = ((1 << overflow_bits_count) - 1);
330 int dropped_bits = number & dropped_bits_mask;
331 number >>= overflow_bits_count;
332 exponent = overflow_bits_count;
333
334 bool zero_tail = true;
335 while (true) {
336 ++current;
337 if (current == end || !isDigit(*current, radix)) break;
338 zero_tail = zero_tail && *current == '0';
339 exponent += radix_log_2;
340 }
341
342 if (!allow_trailing_junk && AdvanceToNonspace(&current, end)) {
343 return JUNK_STRING_VALUE;
344 }
345
346 int middle_value = (1 << (overflow_bits_count - 1));
347 if (dropped_bits > middle_value) {
348 number++; // Rounding up.
349 } else if (dropped_bits == middle_value) {
350 // Rounding to even to consistency with decimals: half-way case rounds
351 // up if significant part is odd and down otherwise.
352 if ((number & 1) != 0 || !zero_tail) {
353 number++; // Rounding up.
354 }
355 }
356
357 // Rounding up may cause overflow.
358 if ((number & ((int64_t)1 << 53)) != 0) {
359 exponent++;
360 number >>= 1;
361 }
362 break;
316 } 363 }
317 ++current; 364 ++current;
318 if (current == end) break; 365 } while (current != end);
366
367 ASSERT(number < ((int64_t)1 << 53));
368 ASSERT(static_cast<int64_t>(static_cast<double>(number)) == number);
369
370 if (exponent == 0) {
371 if (sign) {
372 if (number == 0) return -0.0;
373 number = -number;
374 }
375 return static_cast<double>(number);
319 } 376 }
320 377
321 if (!allow_trailing_junk && AdvanceToNonspace(&current, end)) { 378 ASSERT(number != 0);
322 return JUNK_STRING_VALUE; 379 // The double could be constructed faster from number (mantissa), exponent
323 } 380 // and sign. Assuming it's a rare case more simple code is used.
324 381 return static_cast<double>(sign ? -number : number) * pow(2.0, exponent);
325 if (significant_digits == 0) {
326 return leading_zero ? 0 : JUNK_STRING_VALUE;
327 }
328
329 if (nonzero_digit_dropped) {
330 ASSERT(insignificant_digits > 0);
331 insignificant_digits--;
332 buffer[buffer_pos++] = '1';
333 }
334
335 buffer[buffer_pos] = '\0';
336
337 double result;
338 StringToInt(buffer, begin_pos, 16, &result);
339 if (insignificant_digits > 0) {
340 // Multiplying by a power of 2 doesn't cause a loss of precision.
341 result *= pow(16.0, insignificant_digits);
342 }
343 return result;
344 } 382 }
345 383
346 384
347 // Converts a string to a double value. Assumes the Iterator supports 385 // Converts a string to a double value. Assumes the Iterator supports
348 // the following operations: 386 // the following operations:
349 // 1. current == end (other ops are not allowed), current != end. 387 // 1. current == end (other ops are not allowed), current != end.
350 // 2. *current - gets the current character in the sequence. 388 // 2. *current - gets the current character in the sequence.
351 // 3. ++current (advances the position). 389 // 3. ++current (advances the position).
352 template <class Iterator, class EndMark> 390 template <class Iterator, class EndMark>
353 static double InternalStringToDouble(Iterator current, 391 static double InternalStringToDouble(Iterator current,
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after
406 return buffer_pos > 0 ? -V8_INFINITY : V8_INFINITY; 444 return buffer_pos > 0 ? -V8_INFINITY : V8_INFINITY;
407 } 445 }
408 446
409 bool leading_zero = false; 447 bool leading_zero = false;
410 if (*current == '0') { 448 if (*current == '0') {
411 ++current; 449 ++current;
412 if (current == end) return signed_zero; 450 if (current == end) return signed_zero;
413 451
414 leading_zero = true; 452 leading_zero = true;
415 453
416 // It could be hexadecimal value. 454 // It could be hexadecimal value.
417 if ((flags & ALLOW_HEX) && (*current == 'x' || *current == 'X')) { 455 if ((flags & ALLOW_HEX) && (*current == 'x' || *current == 'X')) {
418 ++current; 456 ++current;
419 if (current == end) return JUNK_STRING_VALUE; // "0x". 457 if (current == end) return JUNK_STRING_VALUE; // "0x".
420 458
421 double result = InternalHexadecimalStringToDouble(current, 459 bool sign = (buffer_pos > 0 && buffer[0] == '-');
422 end, 460 return InternalStringToIntDouble<4>(current,
423 buffer + buffer_pos, 461 end,
424 allow_trailing_junk); 462 sign,
425 return (buffer_pos > 0 && buffer[0] == '-') ? -result : result; 463 allow_trailing_junk);
426 } 464 }
427 465
428 // Ignore leading zeros in the integer part. 466 // Ignore leading zeros in the integer part.
429 while (*current == '0') { 467 while (*current == '0') {
430 ++current; 468 ++current;
431 if (current == end) return signed_zero; 469 if (current == end) return signed_zero;
432 } 470 }
433 } 471 }
434 472
435 bool octal = leading_zero && (flags & ALLOW_OCTALS) != 0; 473 bool octal = leading_zero && (flags & ALLOW_OCTALS) != 0;
(...skipping 117 matching lines...) Expand 10 before | Expand all | Expand 10 after
553 } 591 }
554 592
555 if (!allow_trailing_junk && AdvanceToNonspace(&current, end)) { 593 if (!allow_trailing_junk && AdvanceToNonspace(&current, end)) {
556 return JUNK_STRING_VALUE; 594 return JUNK_STRING_VALUE;
557 } 595 }
558 596
559 parsing_done: 597 parsing_done:
560 exponent += insignificant_digits; 598 exponent += insignificant_digits;
561 599
562 if (octal) { 600 if (octal) {
563 buffer[buffer_pos] = '\0'; 601 bool sign = buffer[0] == '-';
564 // ALLOW_OCTALS is set and there is no '8' or '9' in insignificant 602 int start_pos = (sign ? 1 : 0);
565 // digits. Check significant digits now.
566 char sign = '+';
567 const char* s = buffer;
568 if (*s == '-' || *s == '+') sign = *s++;
569 603
570 double result; 604 return InternalStringToIntDouble<3>(buffer + start_pos,
571 s += StringToInt(s, 0, 8, &result); 605 buffer + buffer_pos,
572 if (!allow_trailing_junk && *s != '\0') return JUNK_STRING_VALUE; 606 sign,
573 607 allow_trailing_junk);
574 if (sign == '-') result = -result;
575 if (insignificant_digits > 0) {
576 result *= pow(8.0, insignificant_digits);
577 }
578 return result;
579 } 608 }
580 609
581 if (nonzero_digit_dropped) { 610 if (nonzero_digit_dropped) {
582 if (insignificant_digits) buffer[buffer_pos++] = '.'; 611 if (insignificant_digits) buffer[buffer_pos++] = '.';
583 buffer[buffer_pos++] = '1'; 612 buffer[buffer_pos++] = '1';
584 } 613 }
585 614
586 // If the number has no more than kMaxDigitsInInt digits and doesn't have 615 // If the number has no more than kMaxDigitsInInt digits and doesn't have
587 // fractional part it could be parsed faster (without checks for 616 // fractional part it could be parsed faster (without checks for
588 // spaces, overflow, etc.). 617 // spaces, overflow, etc.).
(...skipping 417 matching lines...) Expand 10 before | Expand all | Expand 10 after
1006 // Allocate result and fill in the parts. 1035 // Allocate result and fill in the parts.
1007 StringBuilder builder(result_size + 1); 1036 StringBuilder builder(result_size + 1);
1008 builder.AddSubstring(integer_buffer + integer_pos + 1, integer_part_size); 1037 builder.AddSubstring(integer_buffer + integer_pos + 1, integer_part_size);
1009 if (decimal_pos > 0) builder.AddCharacter('.'); 1038 if (decimal_pos > 0) builder.AddCharacter('.');
1010 builder.AddSubstring(decimal_buffer, decimal_pos); 1039 builder.AddSubstring(decimal_buffer, decimal_pos);
1011 return builder.Finalize(); 1040 return builder.Finalize();
1012 } 1041 }
1013 1042
1014 1043
1015 } } // namespace v8::internal 1044 } } // namespace v8::internal
OLDNEW
« no previous file with comments | « no previous file | test/mjsunit/number-tostring.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698