Skip to content

Commit 190c7d6

Browse files
Default implementation of isMultiple(of:) on BinaryInteger
In order to provide source compatibility with existing user types conforming to BinaryInteger, we want to have a default implementation available. It's somewhat difficult to provide a good default implementation that correctly handles arbitrary non-symmetrical ranges in the face of negative divisors, so fall back on testing divisibility of the magnitudes, which avoids the problem. On the plus side, this default implementation works fine for types conforming to UnsignedInteger, which lets us move the FixedWidthInteger implementation down to FixedWidthInteger & SignedInteger, and simplify it in the process.
1 parent aef79d0 commit 190c7d6

File tree

3 files changed

+20
-19
lines changed

3 files changed

+20
-19
lines changed

stdlib/public/core/Integers.swift

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1239,6 +1239,16 @@ extension BinaryInteger {
12391239
-> (quotient: Self, remainder: Self) {
12401240
return (self / rhs, self % rhs)
12411241
}
1242+
1243+
@inlinable
1244+
public func isMultiple(of other: Self) -> Bool {
1245+
// Nothing but zero is a multiple of zero.
1246+
if other == 0 { return self == 0 }
1247+
// Do the test in terms of magnitude, which guarantees there are no other
1248+
// edge cases. If we write this as `self % other` instead, it could trap
1249+
// for types that are not symmetric around zero.
1250+
return self.magnitude % other.magnitude == 0
1251+
}
12421252

12431253
//===----------------------------------------------------------------------===//
12441254
//===--- Homogeneous ------------------------------------------------------===//
@@ -2776,16 +2786,6 @@ extension FixedWidthInteger {
27762786
lhs = _nonMaskingRightShift(lhs, shift)
27772787
}
27782788

2779-
@inlinable
2780-
public func isMultiple(of other: Self) -> Bool {
2781-
// Nothing but zero is a multiple of zero.
2782-
if other == 0 { return self == 0 }
2783-
// Special case to avoid overflow on .min / -1 for signed types.
2784-
if Self.isSigned && other == -1 { return true }
2785-
// Having handled those special cases, this is safe.
2786-
return self % other == 0
2787-
}
2788-
27892789
@inlinable // FIXME(sil-serialize-all)
27902790
@inline(__always)
27912791
public static func _nonMaskingRightShift(_ lhs: Self, _ rhs: Int) -> Self {
@@ -3624,6 +3624,16 @@ extension SignedInteger where Self : FixedWidthInteger {
36243624
public static var min: Self {
36253625
return (-1 as Self) &<< Self._highBitIndex
36263626
}
3627+
3628+
@inlinable
3629+
public func isMultiple(of other: Self) -> Bool {
3630+
// Nothing but zero is a multiple of zero.
3631+
if other == 0 { return self == 0 }
3632+
// Special case to avoid overflow on .min / -1 for signed types.
3633+
if other == -1 { return true }
3634+
// Having handled those special cases, this is safe.
3635+
return self % other == 0
3636+
}
36273637
}
36283638

36293639
/// Returns the given integer as the equivalent value in a different integer

test/Prototypes/BigInt.swift

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -729,11 +729,6 @@ public struct _BigInt<Word: FixedWidthInteger & UnsignedInteger> :
729729
let r = x._internalDivide(by: rhs)
730730
return (x, r)
731731
}
732-
733-
public func isMultiple(of other: _BigInt) -> Bool {
734-
if other == 0 { return self == 0 }
735-
return self % other == 0
736-
}
737732

738733
public static func &=(lhs: inout _BigInt, rhs: _BigInt) {
739734
var lhsTemp = lhs._dataAsTwosComplement()

test/stdlib/Integers.swift.gyb

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -148,10 +148,6 @@ extension MockBinaryInteger : BinaryInteger {
148148
return _value.trailingZeroBitCount
149149
}
150150

151-
func isMultiple(of other: MockBinaryInteger<T>) -> Bool {
152-
return _value.isMultiple(of: other._value)
153-
}
154-
155151
static func + (
156152
lhs: MockBinaryInteger<T>, rhs: MockBinaryInteger<T>
157153
) -> MockBinaryInteger<T> {

0 commit comments

Comments
 (0)