@@ -1544,3 +1544,66 @@ unsigned CountDigits(unsigned num, unsigned base /* = 10 */)
1544
1544
}
1545
1545
1546
1546
#endif // DEBUG
1547
+
1548
+
1549
+ double FloatingPointUtils::convertUInt64ToDouble (unsigned __int64 uIntVal) {
1550
+ __int64 s64 = uIntVal;
1551
+ double d;
1552
+ if (s64 < 0 ) {
1553
+ #if defined(_TARGET_XARCH_)
1554
+ // RyuJIT codegen and clang (or gcc) may produce different results for casting uint64 to
1555
+ // double, and the clang result is more accurate. For example,
1556
+ // 1) (double)0x84595161401484A0UL --> 43e08b2a2c280290 (RyuJIT codegen or VC++)
1557
+ // 2) (double)0x84595161401484A0UL --> 43e08b2a2c280291 (clang or gcc)
1558
+ // If the folding optimization below is implemented by simple casting of (double)uint64_val
1559
+ // and it is compiled by clang, casting result can be inconsistent, depending on whether
1560
+ // the folding optimization is triggered or the codegen generates instructions for casting. //
1561
+ // The current solution is to force the same math as the codegen does, so that casting
1562
+ // result is always consistent.
1563
+
1564
+ // d = (double)(int64_t)uint64 + 0x1p64
1565
+ uint64_t adjHex = 0x43F0000000000000UL ;
1566
+ d = (double )s64 + *(double *)&adjHex;
1567
+ #else
1568
+ d = (double )uIntVal;
1569
+ #endif
1570
+ }
1571
+ else
1572
+ {
1573
+ d = (double )uIntVal;
1574
+ }
1575
+ return d;
1576
+ }
1577
+
1578
+ float FloatingPointUtils::convertUInt64ToFloat (unsigned __int64 u64 ) {
1579
+ double d = convertUInt64ToDouble (u64 );
1580
+ return (float )d;
1581
+ }
1582
+
1583
+ unsigned __int64 FloatingPointUtils::convertDoubleToUInt64 (double d) {
1584
+ unsigned __int64 u64 ;
1585
+ if (d >= 0.0 )
1586
+ {
1587
+ // Work around a C++ issue where it doesn't properly convert large positive doubles
1588
+ const double two63 = 2147483648.0 * 4294967296.0 ;
1589
+ if (d < two63) {
1590
+ u64 = UINT64 (d);
1591
+ }
1592
+ else {
1593
+ // subtract 0x8000000000000000, do the convert then add it back again
1594
+ u64 = INT64 (d - two63) + I64 (0x8000000000000000 );
1595
+ }
1596
+ return u64 ;
1597
+ }
1598
+
1599
+ // This double cast to account for an ECMA spec hole.
1600
+ // When converting from a double to an unsigned the ECMA
1601
+ // spec states that a conforming implementation should
1602
+ // "truncate to zero." However that doesn't make much sense
1603
+ // when the double in question is negative and the target
1604
+ // is unsigned. gcc converts a negative double to zero when
1605
+ // cast to an unsigned. To make gcc conform to MSVC behavior
1606
+ // this cast is necessary.
1607
+ u64 = UINT64 (INT64 (d));
1608
+ return u64 ;
1609
+ }
0 commit comments