@@ -64,66 +64,80 @@ extension BinaryInteger {
64
64
// disagree, we have to adjust q downward and r to match.
65
65
if other. signum ( ) != r. signum ( ) { return q- 1 }
66
66
return q
67
+
67
68
case . up:
68
69
// For rounding up, we want to have r have the opposite sign of
69
70
// other; if not, we adjust q upward and r to match.
70
71
if other. signum ( ) == r. signum ( ) { return q+ 1 }
71
72
return q
73
+
72
74
case . towardZero:
73
75
// This is exactly what the `/` operator did for us.
74
76
return q
75
- case . toOdd:
76
- // If q is already odd, we're done.
77
- if q. _lowWord & 1 == 1 { return q }
78
- // Otherwise, q is even but inexact; it was originally rounded toward
79
- // zero, so rounding away from zero instead will make it odd.
80
- fallthrough
77
+
81
78
case . awayFromZero:
82
- // To round away from zero, we apply the adjustments for both down
83
- // and up.
84
- if other. signum ( ) != r. signum ( ) { return q- 1 }
85
- return q+ 1
86
- case . toNearestOrAwayFromZero:
87
- // For round to nearest or away, the condition we want to satisfy is
88
- // |r| <= |other/2|, with sign(q) != sign(r) when equality holds.
79
+ break
80
+
81
+ case . toNearestOrDown:
82
+ if r. magnitude > other. magnitude. shifted ( rightBy: 1 , rounding: . down) ||
83
+ 2 * r. magnitude == other. magnitude && other. signum ( ) != r. signum ( ) {
84
+ break
85
+ }
86
+ return q
87
+
88
+ case . toNearestOrUp:
89
+ if r. magnitude > other. magnitude. shifted ( rightBy: 1 , rounding: . down) ||
90
+ 2 * r. magnitude == other. magnitude && other. signum ( ) == r. signum ( ) {
91
+ break
92
+ }
93
+ return q
94
+
95
+ case . toNearestOrZero:
96
+ if r. magnitude <= other. magnitude. shifted ( rightBy: 1 , rounding: . down) {
97
+ return q
98
+ }
99
+ // Otherwise, round q away from zero.
100
+
101
+ case . toNearestOrAway:
89
102
if r. magnitude < other. magnitude. shifted ( rightBy: 1 , rounding: . up) {
90
103
return q
91
104
}
92
- // The (q,r) we have does not satisfy the to nearest or away condition;
93
- // round away from zero to choose the other representative of (q, r).
94
- if other. signum ( ) != r. signum ( ) { return q- 1 }
95
- return q+ 1
105
+
96
106
case . toNearestOrEven:
97
- // For round to nearest or away, the condition we want to satisfy is
98
- // |r| <= |other/2|, with q even when equality holds.
99
- if r. magnitude > other. magnitude. shifted ( rightBy: 1 , rounding: . down) ||
100
- 2 * r. magnitude == other. magnitude && q. _lowWord & 1 == 1 {
101
- if ( other > 0 ) != ( r > 0 ) { return q- 1 }
102
- return q+ 1
107
+ // First guarantee that |r| <= |other/2|; if not we have to round away
108
+ // instead, so break to do that.
109
+ if r. magnitude > other. magnitude. shifted ( rightBy: 1 , rounding: . down) ||
110
+ 2 * r. magnitude == other. magnitude && !q. isMultiple ( of: 2 ) {
111
+ break
103
112
}
104
113
return q
114
+
115
+ case . toOdd:
116
+ // If q is already odd, we have the correct result.
117
+ if q. _lowWord & 1 == 1 { return q }
118
+
105
119
case . stochastically:
106
- var qhi : UInt64
120
+ let bmag = other. magnitude
121
+ let rmag = r. magnitude
122
+ var bhi : UInt64
107
123
var rhi : UInt64
108
124
if other. magnitude <= UInt64 . max {
109
- qhi = UInt64 ( other . magnitude )
110
- rhi = UInt64 ( r . magnitude )
125
+ bhi = UInt64 ( bmag )
126
+ rhi = UInt64 ( rmag )
111
127
} else {
112
- // TODO: this is untested currently.
113
- let qmag = other. magnitude
114
- let shift = qmag. _msb - 1
115
- qhi = UInt64 ( truncatingIfNeeded: qmag >> shift)
116
- rhi = UInt64 ( truncatingIfNeeded: r. magnitude >> shift)
128
+ let shift = bmag. _msb - 63
129
+ bhi = UInt64 ( truncatingIfNeeded: bmag >> shift)
130
+ rhi = UInt64 ( truncatingIfNeeded: rmag >> shift)
117
131
}
118
- let ( sum, car) = rhi. addingReportingOverflow ( . random( in: 0 ..< qhi) )
119
- if car || sum >= qhi {
120
- if ( other > 0 ) != ( r > 0 ) { return q- 1 }
121
- return q+ 1
122
- }
123
- return q
132
+ let ( sum, car) = rhi. addingReportingOverflow ( . random( in: 0 ..< bhi) )
133
+ if sum < bhi && !car { return q }
134
+
124
135
case . requireExact:
125
136
preconditionFailure ( " Division was not exact. " )
126
137
}
138
+
139
+ // We didn't have the right result, so round q away from zero.
140
+ return other. signum ( ) == r. signum ( ) ? q+ 1 : q- 1
127
141
}
128
142
129
143
// TODO: make this API and make it possible to implement more efficiently.
@@ -221,68 +235,93 @@ extension SignedInteger {
221
235
// For rounding down, we want to have r match the sign of other
222
236
// rather than self; this means that if the signs of r and other
223
237
// disagree, we have to adjust q downward and r to match.
224
- if other. signum ( ) != r. signum ( ) { return ( q- 1 , r+ other) }
225
- return ( q , r )
238
+ return other. signum ( ) == r. signum ( ) ? ( q , r ) : ( q- 1 , r+ other)
239
+
226
240
case . up:
227
241
// For rounding up, we want to have r have the opposite sign of
228
242
// other; if not, we adjust q upward and r to match.
229
- if other. signum ( ) == r. signum ( ) { return ( q+ 1 , r- other) }
230
- return ( q , r )
243
+ return other. signum ( ) == r. signum ( ) ? ( q+ 1 , r- other) : ( q , r )
244
+
231
245
case . towardZero:
232
246
// This is exactly what the `/` operator did for us.
233
247
return ( q, r)
234
- case . toOdd:
235
- // If q is already odd, we're done.
236
- if q. _lowWord & 1 == 1 { return ( q, r) }
237
- // Otherwise, q is even but inexact; it was originally rounded toward
238
- // zero, so rounding away from zero instead will make it odd.
239
- fallthrough
248
+
240
249
case . awayFromZero:
241
- // To round away from zero, we apply the adjustments for both down
242
- // and up.
243
- if other. signum ( ) != r. signum ( ) { return ( q- 1 , r+ other) }
244
- return ( q+ 1 , r- other)
245
- case . toNearestOrAwayFromZero:
246
- // For round to nearest or away, the condition we want to satisfy is
247
- // |r| <= |other/2|, with sign(q) != sign(r) when equality holds.
248
- if r. magnitude < other. magnitude. shifted ( rightBy: 1 , rounding: . up) {
250
+ break
251
+
252
+ case . toNearestOrDown:
253
+ // If |r| < |other/2|, we already rounded q to nearest. If the are
254
+ // equal and q is negative, then we already broke the tie in the right
255
+ // direction. However, we don't have access to the before-rounding q,
256
+ // which may have rounded up to zero, losing the sign information, so
257
+ // we have to look at other and r instead.
258
+ if 2 * r. magnitude < other. magnitude ||
259
+ 2 * r. magnitude == other. magnitude && other. signum ( ) == r. signum ( ) {
260
+ return ( q, r)
261
+ }
262
+
263
+ case . toNearestOrUp:
264
+ // If |r| < |other/2|, we already rounded q to nearest. If the are
265
+ // equal and q is non-negative, then we already broke the tie in the
266
+ // right direction.
267
+ if 2 * r. magnitude < other. magnitude ||
268
+ 2 * r. magnitude == other. magnitude && other. signum ( ) != r. signum ( ) {
269
+ return ( q, r)
270
+ }
271
+
272
+ case . toNearestOrZero:
273
+ // Check first if |r| <= |other/2|. If this holds, we have already
274
+ // rounded q correctly. Because we're working with magnitudes, we can
275
+ // safely compute 2r without worrying about overflow, even for fixed-
276
+ // width types, because r cannot be .min (because |r| < |other| by
277
+ // construction).
278
+ if 2 * r. magnitude <= other. magnitude {
249
279
return ( q, r)
250
280
}
251
- // The (q,r) we have does not satisfy the to nearest or away condition;
252
- // round away from zero to choose the other representative of (q, r).
253
- if other. signum ( ) != r. signum ( ) { return ( q- 1 , r+ other) }
254
- return ( q+ 1 , r- other)
281
+
282
+ case . toNearestOrAway:
283
+ // Check first if |r| < |other/2|. If this holds, we already rounded
284
+ // q to nearest.
285
+ if 2 * r. magnitude < other. magnitude {
286
+ return ( q, r)
287
+ }
288
+
255
289
case . toNearestOrEven:
256
- // For round to nearest or away, the condition we want to satisfy is
257
- // |r| <= |other/2|, with q even when equality holds.
258
- if r . magnitude > other . magnitude . shifted ( rightBy : 1 , rounding : . down ) ||
259
- 2 * r. magnitude == other. magnitude && q . _lowWord & 1 == 1 {
260
- if ( other > 0 ) != ( r > 0 ) { return ( q - 1 , r + other ) }
261
- return ( q+ 1 , r- other )
290
+ // If |r| < |other/2|, we already rounded q to nearest. If the are
291
+ // equal and q is even, then we already broke the tie in the right
292
+ // direction.
293
+ if 2 * r. magnitude < other. magnitude ||
294
+ 2 * r . magnitude == other . magnitude && q . isMultiple ( of : 2 ) {
295
+ return ( q, r)
262
296
}
263
- return ( q, r)
297
+
298
+ case . toOdd:
299
+ // If q is already odd, we have the correct result.
300
+ if q. _lowWord & 1 == 1 { return ( q, r) }
301
+
264
302
case . stochastically:
265
- var qhi : UInt64
303
+ let bmag = other. magnitude
304
+ let rmag = r. magnitude
305
+ var bhi : UInt64
266
306
var rhi : UInt64
267
307
if other. magnitude <= UInt64 . max {
268
- qhi = UInt64 ( other . magnitude )
269
- rhi = UInt64 ( r . magnitude )
308
+ bhi = UInt64 ( bmag )
309
+ rhi = UInt64 ( rmag )
270
310
} else {
271
- // TODO: this is untested currently.
272
- let qmag = other. magnitude
273
- let shift = qmag. _msb - 1
274
- qhi = UInt64 ( truncatingIfNeeded: qmag >> shift)
275
- rhi = UInt64 ( truncatingIfNeeded: r. magnitude >> shift)
276
- }
277
- let ( sum, car) = rhi. addingReportingOverflow ( . random( in: 0 ..< qhi) )
278
- if car || sum >= qhi {
279
- if ( other > 0 ) != ( r > 0 ) { return ( q- 1 , r+ other) }
280
- return ( q+ 1 , r- other)
311
+ let shift = bmag. _msb - 63
312
+ bhi = UInt64 ( truncatingIfNeeded: bmag >> shift)
313
+ rhi = UInt64 ( truncatingIfNeeded: rmag >> shift)
281
314
}
282
- return ( q, r)
315
+ let ( sum, car) = rhi. addingReportingOverflow ( . random( in: 0 ..< bhi) )
316
+ if sum < bhi && !car { return ( q, r) }
317
+
283
318
case . requireExact:
284
319
preconditionFailure ( " Division was not exact. " )
285
320
}
321
+
322
+ // Fallthrough behavior is to round q away from zero and adjust r to
323
+ // match.
324
+ return other. signum ( ) == r. signum ( ) ? ( q+ 1 , r- other) : ( q- 1 , r+ other)
286
325
}
287
326
}
288
327
0 commit comments