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

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: rebase 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 469 matching lines...) Expand 10 before | Expand all | Expand 10 after
480 value += static_cast<intptr_t>(bigint.GetChunkAt(i)); 480 value += static_cast<intptr_t>(bigint.GetChunkAt(i));
481 } 481 }
482 if (bigint.IsNegative()) { 482 if (bigint.IsNegative()) {
483 value = -value; 483 value = -value;
484 } 484 }
485 return Smi::New(value); 485 return Smi::New(value);
486 } 486 }
487 487
488 488
489 RawDouble* BigintOperations::ToDouble(const Bigint& bigint) { 489 RawDouble* BigintOperations::ToDouble(const Bigint& bigint) {
490 // TODO(floitsch/benl): This is a quick and dirty implementation to unblock 490 ASSERT(IsClamped(bigint));
491 // other areas of the code. It does not handle all bit-twiddling correctly. 491 if (bigint.IsZero()) {
492 const double shift_value = (1 << kDigitBitSize); 492 return Double::New(0.0);
493 double value = 0.0;
494 for (int i = bigint.Length() - 1; i >= 0; i--) {
495 value *= shift_value;
496 value += static_cast<double>(bigint.GetChunkAt(i));
497 } 493 }
494 if (AbsFitsIntoUint64(bigint)) {
495 double absolute_value = static_cast<uint64_t>(AbsToUint64(bigint));
cshapiro 2012/03/23 00:44:39 Is the static_cast<uint64_t>(...) needed? AbsToUi
floitsch 2012/03/26 22:52:05 typo. Should have been static_cast<double>. done.
496 double result = bigint.IsNegative() ? -absolute_value : absolute_value;
497 return Double::New(result);
498 }
499
500 static const int kPhysicalSignificandSize = 52;
501 // The significand size has an additional hidden bit.
502 static const int kSignificandSize = kPhysicalSignificandSize + 1;
503 static const int kExponentBias = 0x3FF + kPhysicalSignificandSize;
504 static const int kMaxExponent = 0x7FF - kExponentBias;
505 static const uint64_t kOne64 = 1;
506 static const uint64_t kInfinityBits =
507 DART_2PART_UINT64_C(0x7FF00000, 00000000);
508
509 // A double is composed of an exponent e and a significand s. Its value equals
510 // s * 2^e. The significand has 53 bits of which the first one must always be
511 // 1 (at least for then numbers we are working with here) and is therefore
512 // omitted. The physical size of the significand is thus 52 bits.
513 // The exponent has 11 bits and is biased by 0x3FF + 52. For example an
514 // exponent e = 10 is written as 0x3FF + 52 + 10 (in the 11 bits that are
515 // reserved for the exponent).
516 // When converting the given bignum to a double we have to pay attention to
517 // the rounding. In particular we have to decide which double to pick if an
518 // input lies exactly between two doubles. As usual with double operations
519 // we pick the double with an even significand in such cases.
520 //
521 // General approach of this algorithm: Get 54 bits (one more than the
522 // significand size) of the bigint. If the last bit is then 1, then (without
523 // knowledge of the remaining bits) we could have a half-way number.
524 // If the second-to-last bit is odd then we know that we have to round up:
525 // if the remaining bits are not zero then the input lies closer to the higher
526 // double. If the remaining bits are zero then we have a half-way case and
527 // we need to round up too (rounding to the even double).
528 // If the second-to-last bit is even then we need to look at the remaining
529 // bits to determine if any of them is not zero. If that's the case then the
530 // number lies closer to the next-higher double. Otherwise we round the
531 // half-way case down to even.
532
533 intptr_t length = bigint.Length();
534 if (((length - 1) * kDigitBitSize) > (kMaxExponent + kSignificandSize)) {
535 // Does not fit into a double.
536 double infinity = bit_cast<double>(kInfinityBits);
537 return Double::New(bigint.IsNegative() ? -infinity : infinity);
538 }
539
540
541 intptr_t digit_index = length - 1;
542 // In order to round correctly we need to look at half-way cases. Therefore we
543 // get kSignificandSize + 1 bits. If the last bit is 1 then we have to look
544 // at the remaining bits to know if we have to round up.
545 int needed_bits = kSignificandSize + 1;
546 ASSERT((kDigitBitSize < needed_bits) && (2 * kDigitBitSize >= needed_bits));
547 uint64_t twice_significand_floor = 0;
cshapiro 2012/03/23 00:44:39 Can we fold this into line 552? There seem to be
floitsch 2012/03/26 22:52:05 done.
548 bool remaining_bits_are_zero = true;
549 intptr_t twice_significant_exponent = 0;
550
551 Chunk firstDigit = bigint.GetChunkAt(digit_index--);
552 twice_significand_floor = firstDigit;
553 needed_bits -= CountBits(firstDigit);
554 if (needed_bits >= kDigitBitSize) {
555 twice_significand_floor <<= kDigitBitSize;
556 twice_significand_floor |= bigint.GetChunkAt(digit_index--);
557 needed_bits -= kDigitBitSize;
558 }
559 if (needed_bits > 0) {
560 ASSERT(needed_bits <= kDigitBitSize);
561 Chunk digit = bigint.GetChunkAt(digit_index--);
562 int discarded_bits_count = kDigitBitSize - needed_bits;
563 twice_significand_floor <<= needed_bits;
564 twice_significand_floor |= digit >> discarded_bits_count;
565 uint64_t discarded_bits_mask = (kOne64 << discarded_bits_count) - 1;
566 remaining_bits_are_zero = ((digit & discarded_bits_mask) == 0);
567 twice_significant_exponent = discarded_bits_count;
568 }
569 ASSERT((twice_significand_floor >> kSignificandSize) == 1);
570
571 twice_significant_exponent += (digit_index + 1) * kDigitBitSize;
572
573 // We might need to round up the significand later.
574 uint64_t significand = twice_significand_floor >> 1;
575 intptr_t exponent = twice_significant_exponent + 1;
576
577 if (exponent >= kMaxExponent) {
578 // Infinity.
579 // Does not fit into a double.
580 double infinity = bit_cast<double>(kInfinityBits);
581 return Double::New(bigint.IsNegative() ? -infinity : infinity);
582 }
583
584 if ((twice_significand_floor & 1) == 1) {
585 bool round_up = false;
586
587 if ((significand & 1) != 0 || !remaining_bits_are_zero) {
588 // Even if the remaining bits are zero we still need to round up since we
589 // want to round to even for half-way cases.
590 round_up = true;
591 } else {
592 // Could be a half-way case. See if the remaining bits are non-zero.
593 for (intptr_t i = 0; i <= digit_index; i++) {
594 if (!remaining_bits_are_zero) break;
595 remaining_bits_are_zero = (bigint.GetChunkAt(i) == 0);
596 }
597 round_up = !remaining_bits_are_zero;
598 }
599
600 if (round_up) {
601 significand++;
602 // It might be that we just went from 53 bits to 54 bits.
603 // Example: After adding 1 to 1FFF..FF (with 53 bits set to 1) we have
604 // 2000..00 (= 2 ^ 54). When adding the exponent and significand together
605 // this will increase the exponent by 1 which is exactly what we want.
606 }
607 }
608
609 ASSERT((significand >> (kSignificandSize - 1)) == 1
610 || significand == kOne64 << kSignificandSize);
611 uint64_t biased_exponent = exponent + kExponentBias;
612 // The significand still has the hidden bit. We simply decrement the biased
613 // exponent by one instead of playing around with the significand.
614 biased_exponent--;
615 // Note that we must use the plus operator instead of bit-or.
616 uint64_t double_bits =
617 (biased_exponent << kPhysicalSignificandSize) + significand;
618
619 double value = bit_cast<double>(double_bits);
498 if (bigint.IsNegative()) { 620 if (bigint.IsNegative()) {
499 value = -value; 621 value = -value;
500 } 622 }
501 return Double::New(value); 623 return Double::New(value);
502 } 624 }
503 625
504 626
505 bool BigintOperations::FitsIntoMint(const Bigint& bigint) { 627 bool BigintOperations::FitsIntoMint(const Bigint& bigint) {
506 intptr_t bigint_length = bigint.Length(); 628 intptr_t bigint_length = bigint.Length();
507 if (bigint_length == 0) { 629 if (bigint_length == 0) {
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
543 return true; 665 return true;
544 } 666 }
545 if (limit < most_significant_digit) { 667 if (limit < most_significant_digit) {
546 return false; 668 return false;
547 } 669 }
548 return !bigint_is_greater; 670 return !bigint_is_greater;
549 } 671 }
550 672
551 673
552 uint64_t BigintOperations::AbsToUint64(const Bigint& bigint) { 674 uint64_t BigintOperations::AbsToUint64(const Bigint& bigint) {
675 ASSERT(AbsFitsIntoUint64(bigint));
553 uint64_t value = 0; 676 uint64_t value = 0;
554 for (int i = bigint.Length() - 1; i >= 0; i--) { 677 for (int i = bigint.Length() - 1; i >= 0; i--) {
555 value <<= kDigitBitSize; 678 value <<= kDigitBitSize;
556 value += static_cast<intptr_t>(bigint.GetChunkAt(i)); 679 value += static_cast<intptr_t>(bigint.GetChunkAt(i));
557 } 680 }
558 return value; 681 return value;
559 } 682 }
560 683
561 684
562 int64_t BigintOperations::ToMint(const Bigint& bigint) { 685 int64_t BigintOperations::ToMint(const Bigint& bigint) {
563 ASSERT(FitsIntoMint(bigint)); 686 ASSERT(FitsIntoMint(bigint));
564 int64_t value = AbsToUint64(bigint); 687 int64_t value = AbsToUint64(bigint);
565 if (bigint.IsNegative()) { 688 if (bigint.IsNegative()) {
566 value = -value; 689 value = -value;
567 } 690 }
568 return value; 691 return value;
569 } 692 }
570 693
571 694
572 bool BigintOperations::FitsIntoUint64(const Bigint& bigint) { 695 bool BigintOperations::AbsFitsIntoUint64(const Bigint& bigint) {
573 if (bigint.IsNegative()) return false;
574 intptr_t b_length = bigint.Length(); 696 intptr_t b_length = bigint.Length();
575 int num_bits = CountBits(bigint.GetChunkAt(b_length - 1)); 697 int num_bits = CountBits(bigint.GetChunkAt(b_length - 1));
576 num_bits += (kDigitBitSize * (b_length - 1)); 698 num_bits += (kDigitBitSize * (b_length - 1));
577 if (num_bits > 64) return false; 699 if (num_bits > 64) return false;
578 return true; 700 return true;
579 } 701 }
580 702
581 703
704 bool BigintOperations::FitsIntoUint64(const Bigint& bigint) {
705 if (bigint.IsNegative()) return false;
706 return AbsFitsIntoUint64(bigint);
707 }
708
709
582 uint64_t BigintOperations::ToUint64(const Bigint& bigint) { 710 uint64_t BigintOperations::ToUint64(const Bigint& bigint) {
583 ASSERT(FitsIntoUint64(bigint)); 711 ASSERT(FitsIntoUint64(bigint));
584 return AbsToUint64(bigint); 712 return AbsToUint64(bigint);
585 } 713 }
586 714
587 715
588 RawBigint* BigintOperations::Multiply(const Bigint& a, const Bigint& b) { 716 RawBigint* BigintOperations::Multiply(const Bigint& a, const Bigint& b) {
589 ASSERT(IsClamped(a)); 717 ASSERT(IsClamped(a));
590 ASSERT(IsClamped(b)); 718 ASSERT(IsClamped(b));
591 719
(...skipping 915 matching lines...) Expand 10 before | Expand all | Expand 10 after
1507 int BigintOperations::CountBits(Chunk digit) { 1635 int BigintOperations::CountBits(Chunk digit) {
1508 int result = 0; 1636 int result = 0;
1509 while (digit != 0) { 1637 while (digit != 0) {
1510 digit >>= 1; 1638 digit >>= 1;
1511 result++; 1639 result++;
1512 } 1640 }
1513 return result; 1641 return result;
1514 } 1642 }
1515 1643
1516 } // namespace dart 1644 } // 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