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

Side by Side Diff: runtime/vm/bigint_operations.cc

Issue 9540015: Implement correct bigint to double conversion. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Address comments. Created 8 years, 9 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 | « runtime/vm/bigint_operations.h ('k') | runtime/vm/bigint_operations_test.cc » ('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 2012 Google Inc. All Rights Reserved. 1 // Copyright 2012 Google Inc. All Rights Reserved.
2 2
3 #include "vm/bigint_operations.h" 3 #include "vm/bigint_operations.h"
4 4
5 #include "platform/utils.h" 5 #include "platform/utils.h"
6 6
7 #include "vm/double_internals.h" 7 #include "vm/double_internals.h"
8 #include "vm/exceptions.h" 8 #include "vm/exceptions.h"
9 #include "vm/object_store.h" 9 #include "vm/object_store.h"
10 #include "vm/zone.h" 10 #include "vm/zone.h"
(...skipping 447 matching lines...) Expand 10 before | Expand all | Expand 10 after
458 value += static_cast<intptr_t>(bigint.GetChunkAt(i)); 458 value += static_cast<intptr_t>(bigint.GetChunkAt(i));
459 } 459 }
460 if (bigint.IsNegative()) { 460 if (bigint.IsNegative()) {
461 value = -value; 461 value = -value;
462 } 462 }
463 return Smi::New(value); 463 return Smi::New(value);
464 } 464 }
465 465
466 466
467 RawDouble* BigintOperations::ToDouble(const Bigint& bigint) { 467 RawDouble* BigintOperations::ToDouble(const Bigint& bigint) {
468 // TODO(floitsch/benl): This is a quick and dirty implementation to unblock 468 ASSERT(IsClamped(bigint));
469 // other areas of the code. It does not handle all bit-twiddling correctly. 469 if (bigint.IsZero()) {
470 const double shift_value = (1 << kDigitBitSize); 470 return Double::New(0.0);
471 double value = 0.0;
472 for (int i = bigint.Length() - 1; i >= 0; i--) {
473 value *= shift_value;
474 value += static_cast<double>(bigint.GetChunkAt(i));
475 } 471 }
472 if (AbsFitsIntoUint64(bigint)) {
473 double absolute_value = static_cast<double>(AbsToUint64(bigint));
474 double result = bigint.IsNegative() ? -absolute_value : absolute_value;
475 return Double::New(result);
476 }
477
478 static const int kPhysicalSignificandSize = 52;
479 // The significand size has an additional hidden bit.
480 static const int kSignificandSize = kPhysicalSignificandSize + 1;
481 static const int kExponentBias = 0x3FF + kPhysicalSignificandSize;
482 static const int kMaxExponent = 0x7FF - kExponentBias;
483 static const uint64_t kOne64 = 1;
484 static const uint64_t kInfinityBits =
485 DART_2PART_UINT64_C(0x7FF00000, 00000000);
486
487 // A double is composed of an exponent e and a significand s. Its value equals
488 // s * 2^e. The significand has 53 bits of which the first one must always be
489 // 1 (at least for then numbers we are working with here) and is therefore
490 // omitted. The physical size of the significand is thus 52 bits.
491 // The exponent has 11 bits and is biased by 0x3FF + 52. For example an
492 // exponent e = 10 is written as 0x3FF + 52 + 10 (in the 11 bits that are
493 // reserved for the exponent).
494 // When converting the given bignum to a double we have to pay attention to
495 // the rounding. In particular we have to decide which double to pick if an
496 // input lies exactly between two doubles. As usual with double operations
497 // we pick the double with an even significand in such cases.
498 //
499 // General approach of this algorithm: Get 54 bits (one more than the
500 // significand size) of the bigint. If the last bit is then 1, then (without
501 // knowledge of the remaining bits) we could have a half-way number.
502 // If the second-to-last bit is odd then we know that we have to round up:
503 // if the remaining bits are not zero then the input lies closer to the higher
504 // double. If the remaining bits are zero then we have a half-way case and
505 // we need to round up too (rounding to the even double).
506 // If the second-to-last bit is even then we need to look at the remaining
507 // bits to determine if any of them is not zero. If that's the case then the
508 // number lies closer to the next-higher double. Otherwise we round the
509 // half-way case down to even.
510
511 intptr_t length = bigint.Length();
512 if (((length - 1) * kDigitBitSize) > (kMaxExponent + kSignificandSize)) {
513 // Does not fit into a double.
514 double infinity = bit_cast<double>(kInfinityBits);
515 return Double::New(bigint.IsNegative() ? -infinity : infinity);
516 }
517
518
519 intptr_t digit_index = length - 1;
520 // In order to round correctly we need to look at half-way cases. Therefore we
521 // get kSignificandSize + 1 bits. If the last bit is 1 then we have to look
522 // at the remaining bits to know if we have to round up.
523 int needed_bits = kSignificandSize + 1;
524 ASSERT((kDigitBitSize < needed_bits) && (2 * kDigitBitSize >= needed_bits));
525 bool discarded_bits_were_zero = true;
526
527 Chunk firstDigit = bigint.GetChunkAt(digit_index--);
528 uint64_t twice_significand_floor = firstDigit;
529 intptr_t twice_significant_exponent = (digit_index + 1) * kDigitBitSize;
530 needed_bits -= CountBits(firstDigit);
531
532 if (needed_bits >= kDigitBitSize) {
533 twice_significand_floor <<= kDigitBitSize;
534 twice_significand_floor |= bigint.GetChunkAt(digit_index--);
535 twice_significant_exponent -= kDigitBitSize;
536 needed_bits -= kDigitBitSize;
537 }
538 if (needed_bits > 0) {
539 ASSERT(needed_bits <= kDigitBitSize);
540 Chunk digit = bigint.GetChunkAt(digit_index--);
541 int discarded_bits_count = kDigitBitSize - needed_bits;
542 twice_significand_floor <<= needed_bits;
543 twice_significand_floor |= digit >> discarded_bits_count;
544 twice_significant_exponent -= needed_bits;
545 uint64_t discarded_bits_mask = (kOne64 << discarded_bits_count) - 1;
546 discarded_bits_were_zero = ((digit & discarded_bits_mask) == 0);
547 }
548 ASSERT((twice_significand_floor >> kSignificandSize) == 1);
549
550 // We might need to round up the significand later.
551 uint64_t significand = twice_significand_floor >> 1;
552 intptr_t exponent = twice_significant_exponent + 1;
553
554 if (exponent >= kMaxExponent) {
555 // Infinity.
556 // Does not fit into a double.
557 double infinity = bit_cast<double>(kInfinityBits);
558 return Double::New(bigint.IsNegative() ? -infinity : infinity);
559 }
560
561 if ((twice_significand_floor & 1) == 1) {
562 bool round_up = false;
563
564 if ((significand & 1) != 0 || !discarded_bits_were_zero) {
565 // Even if the remaining bits are zero we still need to round up since we
566 // want to round to even for half-way cases.
567 round_up = true;
568 } else {
569 // Could be a half-way case. See if the remaining bits are non-zero.
570 for (intptr_t i = 0; i <= digit_index; i++) {
571 if (bigint.GetChunkAt(i) != 0) {
572 round_up = true;
573 break;
574 }
575 }
576 }
577
578 if (round_up) {
579 significand++;
580 // It might be that we just went from 53 bits to 54 bits.
581 // Example: After adding 1 to 1FFF..FF (with 53 bits set to 1) we have
582 // 2000..00 (= 2 ^ 54). When adding the exponent and significand together
583 // this will increase the exponent by 1 which is exactly what we want.
584 }
585 }
586
587 ASSERT((significand >> (kSignificandSize - 1)) == 1
588 || significand == kOne64 << kSignificandSize);
589 uint64_t biased_exponent = exponent + kExponentBias;
590 // The significand still has the hidden bit. We simply decrement the biased
591 // exponent by one instead of playing around with the significand.
592 biased_exponent--;
593 // Note that we must use the plus operator instead of bit-or.
594 uint64_t double_bits =
595 (biased_exponent << kPhysicalSignificandSize) + significand;
596
597 double value = bit_cast<double>(double_bits);
476 if (bigint.IsNegative()) { 598 if (bigint.IsNegative()) {
477 value = -value; 599 value = -value;
478 } 600 }
479 return Double::New(value); 601 return Double::New(value);
480 } 602 }
481 603
482 604
483 bool BigintOperations::FitsIntoMint(const Bigint& bigint) { 605 bool BigintOperations::FitsIntoMint(const Bigint& bigint) {
484 intptr_t bigint_length = bigint.Length(); 606 intptr_t bigint_length = bigint.Length();
485 if (bigint_length == 0) { 607 if (bigint_length == 0) {
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
521 return true; 643 return true;
522 } 644 }
523 if (limit < most_significant_digit) { 645 if (limit < most_significant_digit) {
524 return false; 646 return false;
525 } 647 }
526 return !bigint_is_greater; 648 return !bigint_is_greater;
527 } 649 }
528 650
529 651
530 uint64_t BigintOperations::AbsToUint64(const Bigint& bigint) { 652 uint64_t BigintOperations::AbsToUint64(const Bigint& bigint) {
653 ASSERT(AbsFitsIntoUint64(bigint));
531 uint64_t value = 0; 654 uint64_t value = 0;
532 for (int i = bigint.Length() - 1; i >= 0; i--) { 655 for (int i = bigint.Length() - 1; i >= 0; i--) {
533 value <<= kDigitBitSize; 656 value <<= kDigitBitSize;
534 value += static_cast<intptr_t>(bigint.GetChunkAt(i)); 657 value += static_cast<intptr_t>(bigint.GetChunkAt(i));
535 } 658 }
536 return value; 659 return value;
537 } 660 }
538 661
539 662
540 int64_t BigintOperations::ToMint(const Bigint& bigint) { 663 int64_t BigintOperations::ToMint(const Bigint& bigint) {
541 ASSERT(FitsIntoMint(bigint)); 664 ASSERT(FitsIntoMint(bigint));
542 int64_t value = AbsToUint64(bigint); 665 int64_t value = AbsToUint64(bigint);
543 if (bigint.IsNegative()) { 666 if (bigint.IsNegative()) {
544 value = -value; 667 value = -value;
545 } 668 }
546 return value; 669 return value;
547 } 670 }
548 671
549 672
550 bool BigintOperations::FitsIntoUint64(const Bigint& bigint) { 673 bool BigintOperations::AbsFitsIntoUint64(const Bigint& bigint) {
551 if (bigint.IsNegative()) return false;
552 intptr_t b_length = bigint.Length(); 674 intptr_t b_length = bigint.Length();
553 int num_bits = CountBits(bigint.GetChunkAt(b_length - 1)); 675 int num_bits = CountBits(bigint.GetChunkAt(b_length - 1));
554 num_bits += (kDigitBitSize * (b_length - 1)); 676 num_bits += (kDigitBitSize * (b_length - 1));
555 if (num_bits > 64) return false; 677 if (num_bits > 64) return false;
556 return true; 678 return true;
557 } 679 }
558 680
559 681
682 bool BigintOperations::FitsIntoUint64(const Bigint& bigint) {
683 if (bigint.IsNegative()) return false;
684 return AbsFitsIntoUint64(bigint);
685 }
686
687
560 uint64_t BigintOperations::ToUint64(const Bigint& bigint) { 688 uint64_t BigintOperations::ToUint64(const Bigint& bigint) {
561 ASSERT(FitsIntoUint64(bigint)); 689 ASSERT(FitsIntoUint64(bigint));
562 return AbsToUint64(bigint); 690 return AbsToUint64(bigint);
563 } 691 }
564 692
565 693
566 RawBigint* BigintOperations::Multiply(const Bigint& a, const Bigint& b) { 694 RawBigint* BigintOperations::Multiply(const Bigint& a, const Bigint& b) {
567 ASSERT(IsClamped(a)); 695 ASSERT(IsClamped(a));
568 ASSERT(IsClamped(b)); 696 ASSERT(IsClamped(b));
569 697
(...skipping 947 matching lines...) Expand 10 before | Expand all | Expand 10 after
1517 int BigintOperations::CountBits(Chunk digit) { 1645 int BigintOperations::CountBits(Chunk digit) {
1518 int result = 0; 1646 int result = 0;
1519 while (digit != 0) { 1647 while (digit != 0) {
1520 digit >>= 1; 1648 digit >>= 1;
1521 result++; 1649 result++;
1522 } 1650 }
1523 return result; 1651 return result;
1524 } 1652 }
1525 1653
1526 } // namespace dart 1654 } // namespace dart
OLDNEW
« no previous file with comments | « runtime/vm/bigint_operations.h ('k') | runtime/vm/bigint_operations_test.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698