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

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: 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
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/zone.h" 9 #include "vm/zone.h"
10 10
(...skipping 380 matching lines...) Expand 10 before | Expand all | Expand 10 after
391 value += static_cast<intptr_t>(bigint.GetChunkAt(i)); 391 value += static_cast<intptr_t>(bigint.GetChunkAt(i));
392 } 392 }
393 if (bigint.IsNegative()) { 393 if (bigint.IsNegative()) {
394 value = -value; 394 value = -value;
395 } 395 }
396 return Smi::New(value); 396 return Smi::New(value);
397 } 397 }
398 398
399 399
400 RawDouble* BigintOperations::ToDouble(const Bigint& bigint) { 400 RawDouble* BigintOperations::ToDouble(const Bigint& bigint) {
401 // TODO(floitsch/benl): This is a quick and dirty implementation to unblock 401 if (bigint.IsZero()) {
cshapiro 2012/03/07 21:49:18 This fast path should be expanded to covert values
floitsch 2012/03/20 04:06:48 Done.
402 // other areas of the code. It does not handle all bit-twiddling correctly. 402 return Double::New(0.0);
403 const double shift_value = (1 << kDigitBitSize);
404 double value = 0.0;
405 for (int i = bigint.Length() - 1; i >= 0; i--) {
406 value *= shift_value;
407 value += static_cast<double>(bigint.GetChunkAt(i));
408 } 403 }
404
cshapiro 2012/03/07 21:49:18 How about an early exit here if the value is too l
floitsch 2012/03/20 04:06:48 Done.
405 static const int kPhysicalSignificandSize = 52;
406 // The significand size has an additional hidden bit.
407 static const int kSignificandSize = kPhysicalSignificandSize + 1;
408 static const int kExponentBias = 0x3FF + kPhysicalSignificandSize;
409 static const int kMaxExponent = 0x7FF - kExponentBias;
410 static const uint64_t kOne64 = 1;
411
412 // A double is composed of an exponent e and a significand s. Its value equals
413 // s * 2^e. The significand has 53 bits of which the first one must always be
414 // 1 (at least for then numbers we are working with here) and is therefore
415 // omitted. The physical size of the significand is thus 52 bits.
416 // The exponent has 11 bits and is biased by 0x3FF + 52. For example an
417 // exponent e = 10 is written as 0x3FF + 52 + 10 (in the 11 bits that are
418 // reserved for the exponent).
419 // When converting the given bignum to a double we have to pay attention to
420 // the rounding. In particular we have to decide which double to pick if an
421 // input lies exactly between two doubles. As usual with double operations
422 // we pick the double with an even significand in such cases.
423 //
424 // General approach of this algorithm: Get 54 bits (one more than the
425 // significand size) of the bigint. If the last bit is then 1, then (without
426 // knowledge of the remaining bits) we could have a half-way number.
427 // If the second-to-last bit is odd then we know that we have to round up:
428 // if the remaining bits are not zero then the input lies closer to the higher
429 // double. If the remaining bits are zero then we have a half-way case and
430 // we need to round up too (rounding to the even double).
431 // If the second-to-last bit is even then we need to look at the remaining
432 // bits to determine if any of them is not zero. If that's the case then the
433 // number lies closer to the next-higher double. Otherwise we round the
434 // half-way case down to even.
435
436 uint64_t significand_bits = 0;
437 bool remaining_bits_are_zero = true;
438 intptr_t exponent = 0;
439
cshapiro 2012/03/07 21:49:18 Why not use the ShiftRight method to extract the u
floitsch 2012/03/20 04:06:48 We could, but it seems overkill. I hope the new ve
440 intptr_t digit_index = bigint.Length() - 1;
441 // In order to round correctly we need to look at half-way cases. Therefore we
442 // get kSignificandSize + 1 bits. If the last bit is 1 then we have to look
443 // at the remaining bits to know if we have to round up.
444 int needed_bits = kSignificandSize + 1;
445 while ((digit_index) >= 0 && (needed_bits > 0)) {
446 Chunk digit = bigint.GetChunkAt(digit_index--);
447 if (significand_bits == 0) {
448 significand_bits = digit;
449 while (digit != 0) {
450 needed_bits--;
451 digit >>= 1;
452 }
453 } else if (needed_bits >= kDigitBitSize) {
454 significand_bits <<= kDigitBitSize;
455 significand_bits |= digit;
456 needed_bits -= kDigitBitSize;
457 } else {
458 significand_bits <<= needed_bits;
459 int discarded_bits_count = kDigitBitSize - needed_bits;
460 significand_bits |= digit >> discarded_bits_count;
461 uint64_t discarded_bits_mask = (kOne64 << discarded_bits_count) - 1;
462 remaining_bits_are_zero = ((digit & discarded_bits_mask) == 0);
463 exponent = discarded_bits_count;
464 needed_bits = 0;
465 }
466 }
467 ASSERT(significand_bits != 0);
468 ASSERT(needed_bits > 0 || ((significand_bits >> kSignificandSize) == 1));
469
470 exponent += (digit_index + 1) * kDigitBitSize;
471
472 if (needed_bits > 0) {
473 // The value cannot be a half-way case. Just shift the significand to the
474 // correct position and update the exponent accordingly.
475 significand_bits <<= (needed_bits - 1);
476 exponent -= (needed_bits - 1);
477 } else if ((significand_bits & 1) == 1) {
478 bool round_up = false;
479
480 if ((significand_bits & 2) != 0 || !remaining_bits_are_zero) {
481 // Even if the remaining bits are zero we still need to round up since we
482 // want to round to even for half-way cases.
483 round_up = true;
484 } else {
485 // Could be a half-way case. See if the remaining bits are non-zero.
486 for (intptr_t i = 0; i <= digit_index; i++) {
487 if (!remaining_bits_are_zero) break;
488 remaining_bits_are_zero = (bigint.GetChunkAt(i) == 0);
489 }
490 round_up = !remaining_bits_are_zero;
491 }
492 // Remove the bit we used to determine half-way cases.
493 significand_bits >>= 1;
494 exponent++;
495 if (round_up) {
496 significand_bits += 1;
497 // It might be that we just went from 53 bits to 54 bits.
498 // Example: After adding 1 to 1FFF..FF (with 53 bits set to 1) we have
499 // 2000..00 (= 2 ^ 54). In this case we adjust the exponent and shift to
500 // the right yielding again an 53 bit significand: 1000...00 ('1' followed
501 // by 52 '0' digits).
502 if (significand_bits == (kOne64 << kSignificandSize)) {
503 significand_bits >>= 1;
504 exponent++;
505 }
506 }
507 } else {
508 // Simply round down.
509 significand_bits >>= 1;
510 exponent++;
511 }
512
513 if (exponent >= kMaxExponent) {
514 // Infinity.
515 exponent = kMaxExponent;
516 significand_bits = 0;
517 } else {
518 // Clear the hidden bit in the significand.
519 ASSERT((significand_bits >> (kSignificandSize - 1)) == 1);
520 significand_bits &= ((kOne64 << kPhysicalSignificandSize) - 1);
521 }
522 uint64_t biased_exponent = exponent + kExponentBias;
523 uint64_t double_bits =
524 (biased_exponent << kPhysicalSignificandSize) | significand_bits;
525
526 double value = bit_cast<double>(double_bits);
409 if (bigint.IsNegative()) { 527 if (bigint.IsNegative()) {
410 value = -value; 528 value = -value;
cshapiro 2012/03/07 21:49:18 Why not put the sign bit in when you are construct
floitsch 2012/03/20 04:06:48 It seems cleaner not to duplicate the bit_cast exp
411 } 529 }
412 return Double::New(value); 530 return Double::New(value);
413 } 531 }
414 532
415 533
416 bool BigintOperations::FitsIntoMint(const Bigint& bigint) { 534 bool BigintOperations::FitsIntoMint(const Bigint& bigint) {
417 intptr_t bigint_length = bigint.Length(); 535 intptr_t bigint_length = bigint.Length();
418 if (bigint_length == 0) { 536 if (bigint_length == 0) {
419 return true; 537 return true;
420 } 538 }
(...skipping 994 matching lines...) Expand 10 before | Expand all | Expand 10 after
1415 int BigintOperations::CountBits(Chunk digit) { 1533 int BigintOperations::CountBits(Chunk digit) {
1416 int result = 0; 1534 int result = 0;
1417 while (digit != 0) { 1535 while (digit != 0) {
1418 digit >>= 1; 1536 digit >>= 1;
1419 result++; 1537 result++;
1420 } 1538 }
1421 return result; 1539 return result;
1422 } 1540 }
1423 1541
1424 } // namespace dart 1542 } // namespace dart
OLDNEW
« no previous file with comments | « no previous file | runtime/vm/bigint_operations_test.cc » ('j') | runtime/vm/bigint_operations_test.cc » ('J')

Powered by Google App Engine
This is Rietveld 408576698