Index: runtime/vm/bigint_operations.cc |
diff --git a/runtime/vm/bigint_operations.cc b/runtime/vm/bigint_operations.cc |
index 9d8a2a477d07e42fa2e290b85de9f611255b39aa..0c1847640b2fb3d9a8ba115d9bece068b8acb2dc 100644 |
--- a/runtime/vm/bigint_operations.cc |
+++ b/runtime/vm/bigint_operations.cc |
@@ -487,13 +487,57 @@ RawSmi* BigintOperations::ToSmi(const Bigint& bigint) { |
} |
+static double Uint64ToDouble(uint64_t x) { |
+#if _WIN64 |
+ // For static_cast<double>(x) MSVC x64 generates |
+ // |
+ // cvtsi2sd xmm0, rax |
+ // test rax, rax |
+ // jns done |
+ // addsd xmm0, static_cast<double>(2^64) |
+ // done: |
+ // |
+ // while GCC -m64 generates |
+ // |
+ // test rax, rax |
+ // js negative |
+ // cvtsi2sd xmm0, rax |
+ // jmp done |
+ // negative: |
+ // mov rdx, rax |
+ // shr rdx, 1 |
+ // and eax, 0x1 |
+ // or rdx, rax |
+ // cvtsi2sd xmm0, rdx |
+ // addsd xmm0, xmm0 |
+ // done: |
+ // |
+ // which results in a different rounding. |
+ // |
+ // For consistency between platforms fallback to GCC style converstion |
+ // on Win64. |
+ // |
+ const int64_t y = static_cast<int64_t>(x); |
+ if (y > 0) { |
+ return static_cast<double>(y); |
+ } else { |
+ const double half = static_cast<double>( |
+ static_cast<int64_t>(x >> 1) | (y & 1)); |
+ return half + half; |
+ } |
+#else |
+ return static_cast<double>(x); |
+#endif |
+} |
+ |
+ |
RawDouble* BigintOperations::ToDouble(const Bigint& bigint) { |
ASSERT(IsClamped(bigint)); |
if (bigint.IsZero()) { |
return Double::New(0.0); |
} |
if (AbsFitsIntoUint64(bigint)) { |
- double absolute_value = static_cast<double>(AbsToUint64(bigint)); |
+ double absolute_value = Uint64ToDouble(AbsToUint64(bigint)); |
double result = bigint.IsNegative() ? -absolute_value : absolute_value; |
return Double::New(result); |
} |