mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
Add default implementations for FixedWidthInteger.dividingFullWidth (#70823)
These are provided for FixedWidthInteger & UnsignedInteger (the base implementation, following Knuth's Algorithm D) and SignedInteger (converting to magnitudes and calling the former). Previously no default implementations were available, requiring every type to implement these operations. These defaults will not be optimal for large fixed-width integers, so types vending Int512 or similar integers should still provide their own implementations, but they are unconditionally available as a fallback, which simplifies the process of writing such types, and work well enough as a fallback for modest fixed-width integer types like Int64 or 32b or smaller platforms or Int128 on 64b platforms. Additionally rework the concrete implementations to guarantee that we always trap when the quotient is not representable, and to improve performance for 64b integers on arm64_32, and added some new test coverage for these operations.
This commit is contained in:
@@ -1569,12 +1569,17 @@ ${assignmentOperatorComment(x.operator, True)}
|
||||
#endif
|
||||
% end
|
||||
|
||||
% if bits == 64:
|
||||
#if _pointerBitWidth(_64) || arch(arm64_32)
|
||||
// On 32b architectures we fall back on the generic implementation,
|
||||
// because LLVM doesn't know how to codegen the 128b divide we use.
|
||||
% end
|
||||
/// Returns a tuple containing the quotient and remainder of dividing the
|
||||
/// given value by this value.
|
||||
///
|
||||
/// The resulting quotient must be representable within the bounds of the
|
||||
/// type. If the quotient of dividing `dividend` by this value is too large
|
||||
/// to represent in the type, a runtime error may occur.
|
||||
/// to represent in the type, a runtime error will occur.
|
||||
///
|
||||
/// - Parameter dividend: A tuple containing the high and low parts of a
|
||||
/// double-width integer. The `high` component of the value carries the
|
||||
@@ -1585,34 +1590,58 @@ ${assignmentOperatorComment(x.operator, True)}
|
||||
public func dividingFullWidth(
|
||||
_ dividend: (high: ${Self}, low: ${Self}.Magnitude)
|
||||
) -> (quotient: ${Self}, remainder: ${Self}) {
|
||||
// FIXME(integers): tests
|
||||
% # 128-bit types are not provided by the 32-bit LLVM
|
||||
% if word_bits == 32 and bits == 64:
|
||||
% # FIXME(integers): uncomment the above after using the right conditional
|
||||
% # compilation block to exclude 64-bit Windows, which does not support
|
||||
% # 128-bit operations
|
||||
fatalError("Operation is not supported")
|
||||
% else:
|
||||
// FIXME(integers): handle division by zero and overflows
|
||||
_precondition(self != 0, "Division by zero")
|
||||
% if not signed:
|
||||
_precondition(self > dividend.high, "Quotient is not representable")
|
||||
% end
|
||||
% if dbits <= 64:
|
||||
% DoubleType = ('Int' if signed else 'UInt') + str(dbits)
|
||||
let a = ${DoubleType}(dividend.high) &<< ${bits} |
|
||||
${DoubleType}(dividend.low)
|
||||
let b = ${DoubleType}(self)
|
||||
let (q, r) = a.quotientAndRemainder(dividingBy: b)
|
||||
% if signed:
|
||||
// remainder is guaranteed representable, but the quotient might not be
|
||||
// so we need to use a conversion that will check for us:
|
||||
guard let quotient = ${Self}(exactly: q) else {
|
||||
_preconditionFailure("Quotient is not representable")
|
||||
}
|
||||
return (quotient, ${Self}(truncatingIfNeeded: r))
|
||||
% else:
|
||||
// quotient and remainder are both guaranteed to be representable by
|
||||
// the precondition we enforced above and the definition of division.
|
||||
return (${Self}(truncatingIfNeeded: q), ${Self}(truncatingIfNeeded: r))
|
||||
% end
|
||||
% else:
|
||||
let lhsHigh = Builtin.${z}ext_Int${bits}_Int${dbits}(dividend.high._value)
|
||||
let shift = Builtin.zextOrBitCast_Int8_Int${dbits}(UInt8(${bits})._value)
|
||||
let lhsHighShifted = Builtin.shl_Int${dbits}(lhsHigh, shift)
|
||||
let lhsLow = Builtin.zext_Int${bits}_Int${dbits}(dividend.low._value)
|
||||
let lhs_ = Builtin.or_Int${dbits}(lhsHighShifted, lhsLow)
|
||||
let rhs_ = Builtin.${z}ext_Int${bits}_Int${dbits}(self._value)
|
||||
let a = Builtin.or_Int${dbits}(lhsHighShifted, lhsLow)
|
||||
let b = Builtin.${z}ext_Int${bits}_Int${dbits}(self._value)
|
||||
|
||||
let q = Builtin.${u}div_Int${dbits}(a, b)
|
||||
let r = Builtin.${u}rem_Int${dbits}(a, b)
|
||||
|
||||
let quotient_ = Builtin.${u}div_Int${dbits}(lhs_, rhs_)
|
||||
let remainder_ = Builtin.${u}rem_Int${dbits}(lhs_, rhs_)
|
||||
% if signed:
|
||||
// remainder is guaranteed representable, but the quotient might not be
|
||||
// so we need to check that the conversion succeeds:
|
||||
let (quotient, overflow) =
|
||||
Builtin.s_to_s_checked_trunc_Int${dbits}_Int${bits}(q)
|
||||
_precondition(!Bool(overflow), "Quotient is not representable")
|
||||
% else:
|
||||
// quotient and remainder are both guaranteed to be representable by
|
||||
// the precondition we enforced above and the definition of division.
|
||||
let quotient = Builtin.truncOrBitCast_Int${dbits}_Int${bits}(q)
|
||||
% end
|
||||
let remainder = Builtin.truncOrBitCast_Int${dbits}_Int${bits}(r)
|
||||
|
||||
let quotient = ${Self}(
|
||||
Builtin.truncOrBitCast_Int${dbits}_Int${bits}(quotient_))
|
||||
let remainder = ${Self}(
|
||||
Builtin.truncOrBitCast_Int${dbits}_Int${bits}(remainder_))
|
||||
|
||||
return (quotient: quotient, remainder: remainder)
|
||||
% end
|
||||
return (quotient: Self(quotient), remainder: Self(remainder))
|
||||
% end
|
||||
}
|
||||
% if bits == 64:
|
||||
#endif
|
||||
% end
|
||||
|
||||
/// A representation of this integer with the byte order swapped.
|
||||
@_transparent
|
||||
|
||||
@@ -3383,6 +3383,17 @@ extension FixedWidthInteger {
|
||||
//===--- UnsignedInteger --------------------------------------------------===//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// Implementor's note: UnsignedInteger should have required Magnitude == Self,
|
||||
// because it can necessarily represent the magnitude of every value and every
|
||||
// recursive generic constraint should terminate unless there is a good
|
||||
// semantic reason for it not to do so.
|
||||
//
|
||||
// However, we cannot easily add this constraint because it changes the
|
||||
// mangling of generics constrained on <T: FixedWidthInteger & UnsignedInteger>
|
||||
// to be <T: FixedWidthInteger where T.Magnitude == T>. As a practical matter,
|
||||
// every unsigned type will satisfy this constraint, so converting between
|
||||
// Magnitude and Self in generic code is acceptable.
|
||||
|
||||
/// An integer type that can represent only nonnegative values.
|
||||
public protocol UnsignedInteger: BinaryInteger { }
|
||||
|
||||
@@ -3491,9 +3502,124 @@ extension UnsignedInteger where Self: FixedWidthInteger {
|
||||
/// For unsigned integer types, this value is always `0`.
|
||||
@_transparent
|
||||
public static var min: Self { return 0 }
|
||||
|
||||
@_alwaysEmitIntoClient
|
||||
public func dividingFullWidth(
|
||||
_ dividend: (high: Self, low: Magnitude)
|
||||
) -> (quotient: Self, remainder: Self) {
|
||||
// Validate preconditions to guarantee that the quotient is representable.
|
||||
precondition(self != .zero, "Division by zero")
|
||||
precondition(dividend.high < self,
|
||||
"Dividend.high must be smaller than divisor")
|
||||
// UnsignedInteger should have a Magnitude = Self constraint, but does not,
|
||||
// so we have to do this conversion (we can't easily add the constraint
|
||||
// because it changes how generic signatures constrained to
|
||||
// <FixedWidth & Unsigned> are minimized, which changes the mangling).
|
||||
// In practice, "every" UnsignedInteger type will satisfy this, and if one
|
||||
// somehow manages not to in a way that would break this conversion then
|
||||
// a default implementation of this method never could have worked anyway.
|
||||
let low = Self(dividend.low)
|
||||
|
||||
// The basic algorithm is taken from Knuth (TAoCP, Vol 2, §4.3.1), using
|
||||
// words that are half the size of Self (so the dividend has four words
|
||||
// and the divisor has two). The fact that the denominator has exactly
|
||||
// two words allows for a slight simplification vs. Knuth's Algorithm D,
|
||||
// in that our computed quotient digit is always exactly right, while
|
||||
// in the more general case it can be one too large, requiring a subsequent
|
||||
// borrow.
|
||||
//
|
||||
// Knuth's algorithm (and any long division, really), requires that the
|
||||
// divisor (self) be normalized (meaning that the high-order bit is set).
|
||||
// We begin by counting the leading zeros so we know how many bits we
|
||||
// have to shift to normalize.
|
||||
let lz = leadingZeroBitCount
|
||||
|
||||
// If the divisor is actually a power of two, division is just a shift,
|
||||
// which we can handle much more efficiently. So we do a check for that
|
||||
// case and early-out if possible.
|
||||
if (self &- 1) & self == .zero {
|
||||
let shift = Self.bitWidth - 1 - lz
|
||||
let q = low &>> shift | dividend.high &<< -shift
|
||||
let r = low & (self &- 1)
|
||||
return (q, r)
|
||||
}
|
||||
|
||||
// Shift the divisor left by lz bits to normalize it. We shift the
|
||||
// dividend left by the same amount so that we get the quotient is
|
||||
// preserved (we will have to shift right to recover the remainder).
|
||||
// Note that the right shift `low >> (Self.bitWidth - lz)` is
|
||||
// deliberately a non-masking shift because lz might be zero.
|
||||
let v = self &<< lz
|
||||
let uh = dividend.high &<< lz | low >> (Self.bitWidth - lz)
|
||||
let ul = low &<< lz
|
||||
|
||||
// Now we have a normalized dividend (uh:ul) and divisor (v). Split
|
||||
// v into half-words (vh:vl) so that we can use the "normal" division
|
||||
// on Self as a word / halfword -> halfword division get one halfword
|
||||
// digit of the quotient at a time.
|
||||
let n_2 = Self.bitWidth/2
|
||||
let mask = Self(1) &<< n_2 &- 1
|
||||
let vh = v &>> n_2
|
||||
let vl = v & mask
|
||||
|
||||
// For the (fairly-common) special case where vl is zero, we can simplify
|
||||
// the arithmetic quite a bit:
|
||||
if vl == .zero {
|
||||
let qh = uh / vh
|
||||
let residual = (uh &- qh &* vh) &<< n_2 | ul &>> n_2
|
||||
let ql = residual / vh
|
||||
|
||||
return (
|
||||
// Assemble quotient from half-word digits
|
||||
quotient: qh &<< n_2 | ql,
|
||||
// Compute remainder (we can re-use the residual to make this simpler).
|
||||
remainder: ((residual &- ql &* vh) &<< n_2 | ul & mask) &>> lz
|
||||
)
|
||||
}
|
||||
|
||||
// Helper function: performs a (1½ word)/word division to produce a
|
||||
// half quotient word q. We'll need to use this twice to generate the
|
||||
// full quotient.
|
||||
//
|
||||
// high is the high word of the quotient for this sub-division.
|
||||
// low is the low half-word of the quotient for this sub-division (the
|
||||
// high half of low must be zero).
|
||||
//
|
||||
// returns the quotient half-word digit. In a more general setting, this
|
||||
// computed digit might be one too large, which has to be accounted for
|
||||
// later on (see Knuth, Algorithm D), but when the divisor is only two
|
||||
// half-words (as here), that can never happen, because we use the full
|
||||
// divisor in the check for the while loop.
|
||||
func generateHalfDigit(high: Self, low: Self) -> Self {
|
||||
// Get q̂ satisfying a = vh q̂ + r̂ with 0 ≤ r̂ < vh:
|
||||
var (q̂, r̂) = high.quotientAndRemainder(dividingBy: vh)
|
||||
// Knuth's "Theorem A" establishes that q̂ is an approximation to
|
||||
// the quotient digit q, satisfying q ≤ q̂ ≤ q + 2. We adjust it
|
||||
// downward as needed until we have the correct q.
|
||||
while q̂ > mask || q̂ &* vl > (r̂ &<< n_2 | low) {
|
||||
q̂ &-= 1
|
||||
r̂ &+= vh
|
||||
if r̂ > mask { break }
|
||||
}
|
||||
return q̂
|
||||
}
|
||||
|
||||
// Generate the first quotient digit, subtract off its product with the
|
||||
// divisor to generate the residual, then compute the second quotient
|
||||
// digit from that.
|
||||
let qh = generateHalfDigit(high: uh, low: ul &>> n_2)
|
||||
let residual = (uh &<< n_2 | ul &>> n_2) &- (qh &* v)
|
||||
let ql = generateHalfDigit(high: residual, low: ul & mask)
|
||||
|
||||
return (
|
||||
// Assemble quotient from half-word digits
|
||||
quotient: qh &<< n_2 | ql,
|
||||
// Compute remainder (we can re-use the residual to make this simpler).
|
||||
remainder: ((residual &<< n_2 | ul & mask) &- (ql &* v)) &>> lz
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
//===--- SignedInteger ----------------------------------------------------===//
|
||||
//===----------------------------------------------------------------------===//
|
||||
@@ -3622,6 +3748,40 @@ extension SignedInteger where Self: FixedWidthInteger {
|
||||
// Having handled those special cases, this is safe.
|
||||
return self % other == 0
|
||||
}
|
||||
|
||||
@_alwaysEmitIntoClient
|
||||
public func dividingFullWidth(
|
||||
_ dividend: (high: Self, low: Magnitude)
|
||||
) -> (quotient: Self, remainder: Self) {
|
||||
// Get magnitude of dividend:
|
||||
var magnitudeHigh = Magnitude(truncatingIfNeeded: dividend.high)
|
||||
var magnitudeLow = dividend.low
|
||||
if dividend.high < .zero {
|
||||
let carry: Bool
|
||||
(magnitudeLow, carry) = (~magnitudeLow).addingReportingOverflow(1)
|
||||
magnitudeHigh = ~magnitudeHigh &+ (carry ? 1 : 0)
|
||||
}
|
||||
// Do division on magnitudes (using unsigned implementation):
|
||||
let (unsignedQuotient, unsignedRemainder) = magnitude.dividingFullWidth(
|
||||
(high: magnitudeHigh, low: magnitudeLow)
|
||||
)
|
||||
// Fixup sign: quotient is negative if dividend and divisor disagree.
|
||||
// We will also trap here if the quotient does not fit in Self.
|
||||
let quotient: Self
|
||||
if self ^ dividend.high < .zero {
|
||||
// It is possible that the quotient is representable but its magnitude
|
||||
// is not representable as Self (if quotient is Self.min), so we have
|
||||
// to handle that case carefully here.
|
||||
precondition(unsignedQuotient <= Self.min.magnitude,
|
||||
"Quotient is not representable.")
|
||||
quotient = Self(truncatingIfNeeded: 0 &- unsignedQuotient)
|
||||
} else {
|
||||
quotient = Self(unsignedQuotient)
|
||||
}
|
||||
var remainder = Self(unsignedRemainder)
|
||||
if dividend.high < .zero { remainder = 0 &- remainder }
|
||||
return (quotient, remainder)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the given integer as the equivalent value in a different integer
|
||||
|
||||
141
validation-test/stdlib/IntegerDivision.swift
Normal file
141
validation-test/stdlib/IntegerDivision.swift
Normal file
@@ -0,0 +1,141 @@
|
||||
//===--- IntegerDivision.swift.gyb ----------------------------*- swift -*-===//
|
||||
//
|
||||
// This source file is part of the Swift.org open source project
|
||||
//
|
||||
// Copyright (c) 2024 Apple Inc. and the Swift project authors
|
||||
// Licensed under Apache License v2.0 with Runtime Library Exception
|
||||
//
|
||||
// See https://swift.org/LICENSE.txt for license information
|
||||
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
||||
//
|
||||
//===---------------------------------------------------------------------===//
|
||||
// RUN: %target-run-simple-swift
|
||||
// REQUIRES: executable_test
|
||||
// REQUIRES: optimized_stdlib
|
||||
|
||||
import StdlibUnittest
|
||||
|
||||
var suite = TestSuite("Integer Division")
|
||||
|
||||
suite.test("Int8 division inbounds") {
|
||||
for b in -128 ... 127 {
|
||||
if b == 0 { continue }
|
||||
let lb: Int = (b < 0 ? 128 : -129) * b + 1
|
||||
let ub: Int = (b < 0 ? -129 : 128) * b - 1
|
||||
for a in lb ... ub {
|
||||
let high = Int8(a >> 8)
|
||||
let low = UInt8(a & 0xff)
|
||||
let (q, r) = Int8(b).dividingFullWidth(
|
||||
(high: high, low: low)
|
||||
)
|
||||
expectTrue(r.magnitude < b.magnitude)
|
||||
if r != 0 { expectEqual(r < 0, a < 0) }
|
||||
expectTrue(r == a - b*Int(q))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
suite.test("UInt8 division inbounds") {
|
||||
for b in 0 ... 255 {
|
||||
if b == 0 { continue }
|
||||
for a in 0 ..< (b << 8) {
|
||||
let high = UInt8(a >> 8)
|
||||
let low = UInt8(a & 0xff)
|
||||
let (q, r) = UInt8(b).dividingFullWidth(
|
||||
(high: high, low: low)
|
||||
)
|
||||
expectTrue(r < b)
|
||||
expectTrue(r == a - b*Int(q))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Dead-simple deterministic random source to ensure that we always test
|
||||
// the same "random" values.
|
||||
struct WyRand: RandomNumberGenerator {
|
||||
var state: UInt64
|
||||
mutating func next() -> UInt64 {
|
||||
state &+= 0xa076_1d64_78bd_642f
|
||||
let p = state.multipliedFullWidth(by: state ^ 0xe703_7ed1_a0b4_28db)
|
||||
return p.high ^ p.low
|
||||
}
|
||||
}
|
||||
|
||||
suite.test("Int32 division inbounds") {
|
||||
let step: Int32 = 0x100_0000
|
||||
var g = WyRand(state: 0)
|
||||
for bhi in (-128 as Int32) ... 127 {
|
||||
let b = bhi << 24 | Int32.random(in: 0 ..< step, using: &g)
|
||||
for qhi in (-128 as Int32) ... 127 {
|
||||
let q = qhi << 24 | Int32.random(in: 0 ..< step, using: &g)
|
||||
let rmag = UInt32.random(in: 0 ..< b.magnitude, using: &g)
|
||||
let p = Int64(b)*Int64(q)
|
||||
let r = p < 0 ? -Int32(rmag) : Int32(rmag)
|
||||
let a = p + Int64(r)
|
||||
let high = Int32(a >> 32)
|
||||
let low = UInt32(a & 0xffff_ffff)
|
||||
let observed = b.dividingFullWidth((high: high, low: low))
|
||||
expectEqual(observed.quotient, q)
|
||||
expectEqual(observed.remainder, r)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
suite.test("UInt32 division inbounds") {
|
||||
let step: UInt32 = 0x100_0000
|
||||
var g = WyRand(state: 0)
|
||||
for bhi in UInt32.zero ... 255 {
|
||||
let b = bhi << 24 | UInt32.random(in: 0 ..< step, using: &g)
|
||||
for qhi in UInt32.zero ... 255 {
|
||||
let q = qhi << 24 | UInt32.random(in: 0 ..< step, using: &g)
|
||||
let r = UInt32.random(in: 0 ..< b, using: &g)
|
||||
let a = UInt64(b)*UInt64(q) + UInt64(r)
|
||||
let high = UInt32(a >> 32)
|
||||
let low = UInt32(a & 0xffff_ffff)
|
||||
let observed = b.dividingFullWidth((high: high, low: low))
|
||||
expectEqual(observed.quotient, q)
|
||||
expectEqual(observed.remainder, r)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
suite.test("Int64 division inbounds") {
|
||||
let step: Int64 = 0x100_0000_0000_0000
|
||||
var g = WyRand(state: 0)
|
||||
for bhi in (-128 as Int64) ... 127 {
|
||||
let b = bhi << 56 | Int64.random(in: 0 ..< step, using: &g)
|
||||
for qhi in (-128 as Int64) ... 127 {
|
||||
let q = qhi << 56 | Int64.random(in: 0 ..< step, using: &g)
|
||||
let rmag = UInt64.random(in: 0 ..< b.magnitude, using: &g)
|
||||
let p = b.multipliedFullWidth(by: q)
|
||||
let r = p.high < 0 ? -Int64(rmag) : Int64(rmag)
|
||||
let (low, c) =
|
||||
p.low.addingReportingOverflow(UInt64(truncatingIfNeeded: r))
|
||||
let high = p.high &+ (r >> 63) &+ (c ? 1 : 0)
|
||||
let observed = b.dividingFullWidth((high: high, low: low))
|
||||
expectEqual(observed.quotient, q)
|
||||
expectEqual(observed.remainder, r)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
suite.test("UInt64 division inbounds") {
|
||||
let step: UInt64 = 0x100_0000_0000_0000
|
||||
var g = WyRand(state: 0)
|
||||
for bhi in UInt64.zero ... 255 {
|
||||
let b = bhi << 56 | UInt64.random(in: 0 ..< step, using: &g)
|
||||
for qhi in UInt64.zero ... 255 {
|
||||
let q = qhi << 56 | UInt64.random(in: 0 ..< step, using: &g)
|
||||
let r = UInt64.random(in: 0 ..< b, using: &g)
|
||||
let p = b.multipliedFullWidth(by: q)
|
||||
let (low, c) = p.low.addingReportingOverflow(r)
|
||||
let high = p.high &+ (c ? 1 : 0)
|
||||
let observed = b.dividingFullWidth((high: high, low: low))
|
||||
expectEqual(observed.quotient, q)
|
||||
expectEqual(observed.remainder, r)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
runAllTests()
|
||||
|
||||
110
validation-test/stdlib/IntegerDivisionTraps.swift
Normal file
110
validation-test/stdlib/IntegerDivisionTraps.swift
Normal file
@@ -0,0 +1,110 @@
|
||||
//===--- IntegerDivision.swift.gyb ----------------------------*- swift -*-===//
|
||||
//
|
||||
// This source file is part of the Swift.org open source project
|
||||
//
|
||||
// Copyright (c) 2024 Apple Inc. and the Swift project authors
|
||||
// Licensed under Apache License v2.0 with Runtime Library Exception
|
||||
//
|
||||
// See https://swift.org/LICENSE.txt for license information
|
||||
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
||||
//
|
||||
//===---------------------------------------------------------------------===//
|
||||
// RUN: %target-run-simple-swift
|
||||
// REQUIRES: executable_test
|
||||
// REQUIRES: optimized_stdlib
|
||||
// REQUIRES: long_test
|
||||
// UNSUPPORTED: use_os_stdlib
|
||||
|
||||
import StdlibUnittest
|
||||
|
||||
var suite = TestSuite("Integer Division Traps")
|
||||
|
||||
suite.test("Int8 division lower bound")
|
||||
.forEach(in: Array(-128 ... 127)) { b in
|
||||
if b == 0 { return }
|
||||
let boundary: Int = (b < 0 ? 128 : -129) * b
|
||||
let high = Int8(boundary >> 8)
|
||||
let low = UInt8(boundary & 0xff)
|
||||
expectCrashLater()
|
||||
let (q, _) = Int8(b).dividingFullWidth(
|
||||
(high: high, low: low)
|
||||
)
|
||||
_blackHole(q)
|
||||
}
|
||||
|
||||
suite.test("Int8 division upper bound")
|
||||
.forEach(in: Array(-128 ... 127)) { b in
|
||||
if b == 0 { return }
|
||||
let boundary: Int = (b < 0 ? -129 : 128) * b
|
||||
let high = Int8(boundary >> 8)
|
||||
let low = UInt8(boundary & 0xff)
|
||||
expectCrashLater()
|
||||
let (q, _) = Int8(b).dividingFullWidth(
|
||||
(high: high, low: low)
|
||||
)
|
||||
_blackHole(q)
|
||||
}
|
||||
|
||||
// Dead-simple deterministic random source to ensure that we always test
|
||||
// the same "random" values.
|
||||
struct WyRand: RandomNumberGenerator {
|
||||
var state: UInt64
|
||||
mutating func next() -> UInt64 {
|
||||
state &+= 0xa076_1d64_78bd_642f
|
||||
let p = state.multipliedFullWidth(by: state ^ 0xe703_7ed1_a0b4_28db)
|
||||
return p.high ^ p.low
|
||||
}
|
||||
}
|
||||
|
||||
suite.test("Int32 division lower bound")
|
||||
.forEach(in: Array((-128 as Int32) ... 127)) { bhi in
|
||||
var g = WyRand(state: UInt64(truncatingIfNeeded: bhi))
|
||||
let b = bhi << 24 | Int32.random(in: 0 ..< 0x100_0000, using: &g)
|
||||
let boundary = (b < 0 ? 0x1_0000_0000 : -0x1_0000_0001) * Int64(b)
|
||||
let high = Int32(boundary >> 32)
|
||||
let low = UInt32(boundary & 0xffff_ffff)
|
||||
expectCrashLater()
|
||||
let (q, _) = b.dividingFullWidth(
|
||||
(high: high, low: low)
|
||||
)
|
||||
_blackHole(q)
|
||||
}
|
||||
|
||||
suite.test("Int32 division upper bound")
|
||||
.forEach(in: Array((-128 as Int32) ... 127)) { bhi in
|
||||
var g = WyRand(state: UInt64(truncatingIfNeeded: bhi))
|
||||
let b = bhi << 24 | Int32.random(in: 0 ..< 0x100_0000, using: &g)
|
||||
let boundary = (b < 0 ? -0x1_0000_0001 : -0x1_0000_0000) * Int64(b)
|
||||
let high = Int32(boundary >> 32)
|
||||
let low = UInt32(boundary & 0xffff_ffff)
|
||||
expectCrashLater()
|
||||
let (q, _) = b.dividingFullWidth(
|
||||
(high: high, low: low)
|
||||
)
|
||||
_blackHole(q)
|
||||
}
|
||||
|
||||
suite.test("UInt32 division upper bound")
|
||||
.forEach(in: Array(UInt32.zero ... 255)) { bhi in
|
||||
var g = WyRand(state: UInt64(truncatingIfNeeded: bhi))
|
||||
let b = bhi << 24 | UInt32.random(in: 0 ..< 0x100_0000, using: &g)
|
||||
expectCrashLater()
|
||||
let (q, _) = b.dividingFullWidth(
|
||||
(high: b, low: UInt32.random(in: 0 ... .max, using: &g))
|
||||
)
|
||||
_blackHole(q)
|
||||
}
|
||||
|
||||
suite.test("UInt64 division upper bound")
|
||||
.forEach(in: Array(UInt64.zero ... 255)) { bhi in
|
||||
var g = WyRand(state: UInt64(truncatingIfNeeded: bhi))
|
||||
let b = bhi << 56 | UInt64.random(in: 0 ..< 0x100_0000_0000_0000, using: &g)
|
||||
expectCrashLater()
|
||||
let (q, _) = b.dividingFullWidth(
|
||||
(high: b, low: UInt64.random(in: 0 ... .max, using: &g))
|
||||
)
|
||||
_blackHole(q)
|
||||
}
|
||||
|
||||
runAllTests()
|
||||
|
||||
Reference in New Issue
Block a user