Skip to content

Commit 683c8f2

Browse files
committed
Cleanup and bug fix for wide stochastic rounding path.
1 parent c69fea9 commit 683c8f2

File tree

2 files changed

+46
-31
lines changed

2 files changed

+46
-31
lines changed

Sources/IntegerUtilities/DivideWithRounding.swift

+20-28
Original file line numberDiff line numberDiff line change
@@ -117,24 +117,20 @@ extension BinaryInteger {
117117
if q._lowWord & 1 == 1 { return q }
118118

119119
case .stochastically:
120-
var qhi: UInt64
120+
let bmag = other.magnitude
121+
let rmag = r.magnitude
122+
var bhi: UInt64
121123
var rhi: UInt64
122124
if other.magnitude <= UInt64.max {
123-
qhi = UInt64(other.magnitude)
124-
rhi = UInt64(r.magnitude)
125+
bhi = UInt64(bmag)
126+
rhi = UInt64(rmag)
125127
} else {
126-
// TODO: this is untested currently.
127-
let qmag = other.magnitude
128-
let shift = qmag._msb - 1
129-
qhi = UInt64(truncatingIfNeeded: qmag >> shift)
130-
rhi = UInt64(truncatingIfNeeded: r.magnitude >> shift)
128+
let shift = bmag._msb - 63
129+
bhi = UInt64(truncatingIfNeeded: bmag >> shift)
130+
rhi = UInt64(truncatingIfNeeded: rmag >> shift)
131131
}
132-
let (sum, car) = rhi.addingReportingOverflow(.random(in: 0 ..< qhi))
133-
if car || sum >= qhi {
134-
if (other > 0) != (r > 0) { return q-1 }
135-
return q+1
136-
}
137-
return q
132+
let (sum, car) = rhi.addingReportingOverflow(.random(in: 0 ..< bhi))
133+
if sum < bhi && !car { return q }
138134

139135
case .requireExact:
140136
preconditionFailure("Division was not exact.")
@@ -304,24 +300,20 @@ extension SignedInteger {
304300
if q._lowWord & 1 == 1 { return (q, r) }
305301

306302
case .stochastically:
307-
var qhi: UInt64
303+
let bmag = other.magnitude
304+
let rmag = r.magnitude
305+
var bhi: UInt64
308306
var rhi: UInt64
309307
if other.magnitude <= UInt64.max {
310-
qhi = UInt64(other.magnitude)
311-
rhi = UInt64(r.magnitude)
308+
bhi = UInt64(bmag)
309+
rhi = UInt64(rmag)
312310
} else {
313-
// TODO: this is untested currently.
314-
let qmag = other.magnitude
315-
let shift = qmag._msb - 1
316-
qhi = UInt64(truncatingIfNeeded: qmag >> shift)
317-
rhi = UInt64(truncatingIfNeeded: r.magnitude >> shift)
311+
let shift = bmag._msb - 63
312+
bhi = UInt64(truncatingIfNeeded: bmag >> shift)
313+
rhi = UInt64(truncatingIfNeeded: rmag >> shift)
318314
}
319-
let (sum, car) = rhi.addingReportingOverflow(.random(in: 0 ..< qhi))
320-
if car || sum >= qhi {
321-
if (other > 0) != (r > 0) { return (q-1, r+other) }
322-
return (q+1, r-other)
323-
}
324-
return (q, r)
315+
let (sum, car) = rhi.addingReportingOverflow(.random(in: 0 ..< bhi))
316+
if sum < bhi && !car { return (q, r) }
325317

326318
case .requireExact:
327319
preconditionFailure("Division was not exact.")

Tests/IntegerUtilitiesTests/DivideTests.swift

+26-3
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
//===----------------------------------------------------------------------===//
1212

1313
import IntegerUtilities
14+
import _TestSupport
1415
import XCTest
1516

1617
final class IntegerUtilitiesDivideTests: XCTestCase {
@@ -435,6 +436,27 @@ final class IntegerUtilitiesDivideTests: XCTestCase {
435436
testDivideStochastic(values)
436437
testDivideExact(values)
437438
}
439+
440+
func testDivideInt128() {
441+
var values = [DoubleWidth<Int64>](repeating: 0, count: 64)
442+
for i in 0 ..< values.count {
443+
while values[i] == 0 {
444+
values[i] = .random(in: .min ... .max)
445+
}
446+
}
447+
testDivideDown(values)
448+
testDivideUp(values)
449+
testDivideTowardZero(values)
450+
testDivideAwayFromZero(values)
451+
testDivideToNearestOrDown(values)
452+
testDivideToNearestOrUp(values)
453+
testDivideToNearestOrZero(values)
454+
testDivideToNearestOrAway(values)
455+
testDivideToNearestOrEven(values)
456+
testDivideToOdd(values)
457+
testDivideStochastic(values)
458+
testDivideExact(values)
459+
}
438460

439461
func divideUInt8(_ a: UInt8, _ b: UInt8, rounding rule: RoundingRule) {
440462
let expected = UInt8(Int16(a).divided(by: Int16(b), rounding: rule).quotient)
@@ -477,11 +499,12 @@ final class IntegerUtilitiesDivideTests: XCTestCase {
477499
// check that it is acceptably close to the exact expected value; simple
478500
// use of any deterministic rounding rule will not achieve this.
479501
func testStochasticDivide<T: FixedWidthInteger>(_ a: T, _ b: T) -> Bool {
502+
let trunc = a/b
480503
let sum = (0..<1024).reduce(into: 0.0) { sum, _ in
481-
let rounded = a.divided(by: b, rounding: .stochastically)
482-
sum += Double(rounded)
504+
let rounding = a.divided(by: b, rounding: .stochastically) - trunc
505+
sum += Double(rounding)
483506
}
484-
let expected = 1024 * Double(a) / Double(b)
507+
let expected = 1024*Double(a%b)/Double(b)
485508
let difference = abs(sum - expected)
486509
// Waving my hands slightly instead of giving a precise explanation
487510
// here, the expectation is that difference should be about

0 commit comments

Comments
 (0)