@@ -695,17 +695,8 @@ object RuntimeLong {
695
695
a.lo
696
696
697
697
@ inline
698
- def toDouble (a : RuntimeLong ): Double = {
699
- val lo = a.lo
700
- val hi = a.hi
701
- if (hi < 0 ) {
702
- // We need unsignedToDoubleApprox specifically for MinValue
703
- val neg = inline_negate(lo, hi)
704
- - unsignedToDoubleApprox(neg.lo, neg.hi)
705
- } else {
706
- nonNegativeToDoubleApprox(lo, hi)
707
- }
708
- }
698
+ def toDouble (a : RuntimeLong ): Double =
699
+ signedToDoubleApprox(a.lo, a.hi)
709
700
710
701
@ inline
711
702
def toFloat (a : RuntimeLong ): Float =
@@ -1197,7 +1188,7 @@ object RuntimeLong {
1197
1188
1198
1189
/** Converts an unsigned safe double into its Double representation. */
1199
1190
@ inline def asUnsignedSafeDouble (lo : Int , hi : Int ): Double =
1200
- nonNegativeToDoubleApprox (lo, hi)
1191
+ signedToDoubleApprox (lo, hi) // can use either signed or unsigned here; signed folds better
1201
1192
1202
1193
/** Converts an unsigned safe double into its RuntimeLong representation. */
1203
1194
@ inline def fromUnsignedSafeDouble (x : Double ): RuntimeLong =
@@ -1215,14 +1206,41 @@ object RuntimeLong {
1215
1206
@ inline def unsignedToDoubleApprox (lo : Int , hi : Int ): Double =
1216
1207
uintToDouble(hi) * TwoPow32 + uintToDouble(lo)
1217
1208
1218
- /** Approximates a non-negative (lo, hi) with a Double.
1209
+ /** Approximates a signed (lo, hi) with a Double.
1219
1210
*
1220
1211
* If `hi` is known to be non-negative, this method is equivalent to
1221
1212
* `unsignedToDoubleApprox`, but it can fold away part of the computation if
1222
1213
* `hi` is in fact constant.
1223
1214
*/
1224
- @ inline def nonNegativeToDoubleApprox (lo : Int , hi : Int ): Double =
1215
+ @ inline def signedToDoubleApprox (lo : Int , hi : Int ): Double = {
1216
+ /* We note a_u the mathematical value of a when interpreted as an unsigned
1217
+ * quantity, and a_s when interpreted as a signed quantity.
1218
+ *
1219
+ * For x = (lo, hi), the result must be the correctly rounded value of x_s.
1220
+ *
1221
+ * If x_s >= 0, then hi_s >= 0. The obvious mathematical value of x_s is
1222
+ * x_s = hi_s * 2^32 + lo_u
1223
+ *
1224
+ * If x_s < 0, then hi_s < 0. The fundamental definition of two's
1225
+ * completement means that
1226
+ * x_s = -2^64 + hi_u * 2^32 + lo_u
1227
+ * Likewise,
1228
+ * hi_s = -2^32 + hi_u
1229
+ *
1230
+ * Now take the computation for the x_s >= 0 case, but substituting values
1231
+ * for the negative case:
1232
+ * hi_s * 2^32 + lo_u
1233
+ * = (-2^32 + hi_u) * 2^32 + lo_u
1234
+ * = (-2^64 + hi_u * 2^32) + lo_u
1235
+ * which is the correct mathematical result for x_s in the negative case.
1236
+ *
1237
+ * Therefore, we can always compute
1238
+ * x_s = hi_s * 2^32 + lo_u
1239
+ * When computed with `Double` values, only the last `+` can be inexact,
1240
+ * hence the result is correctly round.
1241
+ */
1225
1242
hi.toDouble * TwoPow32 + uintToDouble(lo)
1243
+ }
1226
1244
1227
1245
/** Interprets an `Int` as an unsigned integer and returns its value as a
1228
1246
* `Double`.
0 commit comments