Skip to content

Commit 97d145c

Browse files
Bug fixes and improvements for DoubleWidth prototype (#75209)
* Bug fixes and improvements for DoubleWidth prototype Previously multipliedReportingOverflow and masking shifts were implemented incorrectly for signed types and non-power-of-two bitWidths, respectively. Address those two bugs and additional implement &+, &-, and &*. * Restore line accidentally deleted
1 parent a649878 commit 97d145c

File tree

1 file changed

+42
-20
lines changed

1 file changed

+42
-20
lines changed

test/Prototypes/DoubleWidth.swift.gyb

Lines changed: 42 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -311,17 +311,11 @@ extension DoubleWidth : FixedWidthInteger {
311311
by rhs: DoubleWidth
312312
) -> (partialValue: DoubleWidth, overflow: Bool) {
313313
let (carry, product) = multipliedFullWidth(by: rhs)
314-
let result = DoubleWidth(truncatingIfNeeded: product)
315-
316-
let isNegative = DoubleWidth.isSigned &&
317-
(self < (0 as DoubleWidth)) != (rhs < (0 as DoubleWidth))
318-
let didCarry = isNegative
319-
? carry != ~(0 as DoubleWidth)
320-
: carry != (0 as DoubleWidth)
321-
let hadPositiveOverflow =
322-
DoubleWidth.isSigned && !isNegative && product.leadingZeroBitCount == 0
323-
324-
return (result, didCarry || hadPositiveOverflow)
314+
let partialValue = DoubleWidth(truncatingIfNeeded: product)
315+
// Overflow has occured if carry is not just the sign-extension of
316+
// partialValue (which is zero when Base is unsigned).
317+
let overflow = carry != (partialValue >> DoubleWidth.bitWidth)
318+
return (partialValue, overflow)
325319
}
326320

327321
public func quotientAndRemainder(
@@ -473,18 +467,21 @@ extension DoubleWidth : FixedWidthInteger {
473467

474468
/// Returns this value "masked" by its bit width.
475469
///
476-
/// "Masking" notionally involves repeatedly incrementing or decrementing this
477-
/// value by `self.bitWidth` until the result is contained in the range
478-
/// `0..<self.bitWidth`.
470+
/// "Masking" notionally involves repeatedly incrementing or decrementing
471+
/// this value by `self.bitWidth` until the result is contained in the
472+
/// range `0..<self.bitWidth`.
479473
internal func _masked() -> DoubleWidth {
480-
// FIXME(integers): test types with bit widths that aren't powers of 2
474+
let bits = DoubleWidth(DoubleWidth.bitWidth)
481475
if DoubleWidth.bitWidth.nonzeroBitCount == 1 {
482-
return self & DoubleWidth(DoubleWidth.bitWidth &- 1)
483-
}
484-
if DoubleWidth.isSigned && self._storage.high < (0 as High) {
485-
return self % DoubleWidth(DoubleWidth.bitWidth) + self
476+
return self & (bits &- 1)
486477
}
487-
return self % DoubleWidth(DoubleWidth.bitWidth)
478+
let reduced = self % bits
479+
// bitWidth is always positive, but the value being reduced might have
480+
// been negative, in which case reduced will also be negative. We need
481+
// the representative in [0, bitWidth), so conditionally add the count
482+
// to get the positive residue.
483+
if Base.isSigned && reduced < 0 { return reduced &+ bits }
484+
return reduced
488485
}
489486

490487
public static func &<<=(lhs: inout DoubleWidth, rhs: DoubleWidth) {
@@ -555,6 +552,31 @@ binaryOperators = [
555552
}
556553
% end
557554

555+
public static func &+(
556+
lhs: DoubleWidth, rhs: DoubleWidth
557+
) -> DoubleWidth {
558+
let (low, carry) = lhs.low.addingReportingOverflow(rhs.low)
559+
let high = lhs.high &+ rhs.high &+ (carry ? 1 : 0)
560+
return DoubleWidth(high, low)
561+
}
562+
563+
public static func &-(
564+
lhs: DoubleWidth, rhs: DoubleWidth
565+
) -> DoubleWidth {
566+
let (low, borrow) = lhs.low.subtractingReportingOverflow(rhs.low)
567+
let high = lhs.high &- rhs.high &- (borrow ? 1 : 0)
568+
return DoubleWidth(high, low)
569+
}
570+
571+
public static func &*(
572+
lhs: DoubleWidth, rhs: DoubleWidth
573+
) -> DoubleWidth {
574+
let p00 = lhs.low.multipliedFullWidth(by: rhs.low)
575+
let p10 = lhs.high &* Base(truncatingIfNeeded: rhs.low)
576+
let p01 = Base(truncatingIfNeeded: lhs.low) &* rhs.high
577+
return DoubleWidth(p10 &+ p01 &+ Base(truncatingIfNeeded: p00.high), p00.low)
578+
}
579+
558580
public init(_truncatingBits bits: UInt) {
559581
_storage.low = Low(_truncatingBits: bits)
560582
_storage.high = High(_truncatingBits: bits >> UInt(Low.bitWidth))

0 commit comments

Comments
 (0)