diff --git a/stdlib/public/core/DoubleWidth.swift.gyb b/stdlib/public/core/DoubleWidth.swift.gyb
index ff6f8c648f75a..f82df31d6dfdf 100644
--- a/stdlib/public/core/DoubleWidth.swift.gyb
+++ b/stdlib/public/core/DoubleWidth.swift.gyb
@@ -12,7 +12,8 @@
/// A fixed-width integer that is twice the size of its base type.
@_fixed_layout // FIXME(sil-serialize-all)
-public struct DoubleWidth : _ExpressibleByBuiltinIntegerLiteral
+public struct DoubleWidth
+ : _ExpressibleByBuiltinIntegerLiteral
where Base.Words : Collection, Base.Magnitude.Words : Collection {
public typealias High = Base
@@ -52,11 +53,25 @@ public struct DoubleWidth : _ExpressibleByBuiltinInteg
}
}
-extension DoubleWidth: Comparable {
+extension DoubleWidth : CustomStringConvertible {
+ @_inlineable // FIXME(sil-serialize-all)
+ public var description: String {
+ return String(self, radix: 10)
+ }
+}
+
+extension DoubleWidth : CustomDebugStringConvertible {
+ @_inlineable // FIXME(sil-serialize-all)
+ public var debugDescription: String {
+ return "(\(_storage.high), \(_storage.low))"
+ }
+}
+
+extension DoubleWidth : Comparable {
@_inlineable // FIXME(sil-serialize-all)
public static func ==(lhs: DoubleWidth, rhs: DoubleWidth) -> Bool {
- return (lhs._storage.high == rhs._storage.high) &&
- (lhs._storage.low == rhs._storage.low)
+ return lhs._storage.low == rhs._storage.low &&
+ lhs._storage.high == rhs._storage.high
}
@_inlineable // FIXME(sil-serialize-all)
@@ -71,19 +86,29 @@ extension DoubleWidth: Comparable {
}
}
+extension DoubleWidth : Hashable {
+ @_inlineable // FIXME(sil-serialize-all)
+ public var hashValue: Int {
+ var result = 0
+ result = _combineHashValues(result, _storage.high.hashValue)
+ result = _combineHashValues(result, _storage.low.hashValue)
+ result = _mixInt(result)
+ return result
+ }
+}
-extension DoubleWidth: Numeric {
+extension DoubleWidth : Numeric {
public typealias Magnitude = DoubleWidth
@_inlineable // FIXME(sil-serialize-all)
- public var magnitude: DoubleWidth {
+ public var magnitude: Magnitude {
+ let result = Magnitude((
+ Low(truncatingIfNeeded: _storage.high), _storage.low))
if Base.isSigned && _storage.high < (0 as High) {
- return self == .min
- ? DoubleWidth.max.magnitude &+ 1
- : (0 - self).magnitude
+ return ~result &+ 1
+ } else {
+ return result
}
- return DoubleWidth((
- _storage.high.magnitude, _storage.low.magnitude))
}
@_inlineable // FIXME(sil-serialize-all)
@@ -102,97 +127,26 @@ extension DoubleWidth: Numeric {
@_inlineable // FIXME(sil-serialize-all)
public init?(exactly source: T) {
- // Can't represent a negative 'source' if Base is unsigned
- guard source >= 0 || DoubleWidth.isSigned else { return nil }
+ // Can't represent a negative 'source' if Base is unsigned.
+ guard DoubleWidth.isSigned || source >= 0 else { return nil }
- // Is 'source' is entirely representable in Low?
+ // Is 'source' entirely representable in Low?
if let low = Low(exactly: source.magnitude) {
- if source < (0 as T) {
- self.init((~0, ~low + 1))
- } else {
- self.init((0, low))
- }
+ self.init(source < (0 as T) ? (~0, ~low &+ 1) : (0, low))
} else {
- // At this point we know source's bitWidth > Base.bitWidth, or else
- // we would've taken the first branch.
+ // At this point we know source.bitWidth > Base.bitWidth, or else we
+ // would've taken the first branch.
let lowInT = source & T(~0 as Low)
- let highInT = source >> High.bitWidth
+ let highInT = source >> Low.bitWidth
- let low = Low(lowInT.magnitude)
+ let low = Low(lowInT)
guard let high = High(exactly: highInT) else { return nil }
self.init((high, low))
}
}
}
-extension DoubleWidth {
- @_inlineable // FIXME(sil-serialize-all)
- public init(_ source: T) {
- fatalError()
- }
-
- @_inlineable // FIXME(sil-serialize-all)
- public init?(exactly source: T) {
- fatalError()
- }
-
- @_inlineable // FIXME(sil-serialize-all)
- public init(_ source: T)
- where T.RawSignificand : FixedWidthInteger
- {
- _precondition(source.isFinite, "Can't create a DoubleWidth from a non-finite value")
- self.init(exactly: source.rounded(.towardZero))!
- }
-
- @_inlineable // FIXME(sil-serialize-all)
- public init?(exactly source: T)
- where T.RawSignificand : FixedWidthInteger
- {
- // Need a finite value
- guard source.isFinite else { return nil }
-
- // Don't need to go further with zero.
- if source.isZero {
- self.init(0)
- return
- }
-
- // Need a value with a non-negative exponent
- guard source.exponent >= 0 else { return nil }
-
- typealias Raw = T.RawSignificand
- let bitPattern = source.significandBitPattern |
- ((1 as Raw) &<< Raw(T.significandBitCount))
- let offset = T.significandBitCount - Int(source.exponent)
-
- // FIXME: spurious compile error when 'where' clause above is removed:
- // error: non-nominal type 'T.RawSignificand' does not support explicit initialization
- let fractionPart: Raw = bitPattern &<< Raw(Raw.bitWidth - offset)
- guard fractionPart == (0 as Raw) else {
- return nil
- }
-
- let integerPart: Raw = bitPattern &>> Raw(offset)
-
- // Should have caught any actual zero values above
- _sanityCheck(integerPart > (0 as Raw))
-
- if source.sign == .minus {
- if !DoubleWidth.isSigned || integerPart &- 1 > DoubleWidth.max {
- return nil
- }
- // Have to juggle, or else the intermediate step of creating a value
- // with Self.min's magnitude will overflow. It's okay to use wrapping
- // subtraction because integerPart > 0.
- self.init(integerPart &- 1)
- self = 0 &- self &- 1
- } else {
- self.init(exactly: integerPart)
- }
- }
-}
-
-extension DoubleWidth: FixedWidthInteger {
+extension DoubleWidth : FixedWidthInteger {
@_fixed_layout // FIXME(sil-serialize-all)
public struct Words : Collection {
public enum _IndexValue {
@@ -232,7 +186,7 @@ extension DoubleWidth: FixedWidthInteger {
@_inlineable // FIXME(sil-serialize-all)
public init(_ value: DoubleWidth) {
- // multiples of word-size only
+ // Multiples of word size only.
guard Base.bitWidth == Base.Magnitude.bitWidth &&
(UInt.bitWidth % Base.bitWidth == 0 ||
Base.bitWidth % UInt.bitWidth == 0) else {
@@ -300,8 +254,6 @@ extension DoubleWidth: FixedWidthInteger {
return Base.isSigned
}
- // fixed width
- //
@_inlineable // FIXME(sil-serialize-all)
public static var max: DoubleWidth {
return self.init((High.max, Low.max))
@@ -314,7 +266,7 @@ extension DoubleWidth: FixedWidthInteger {
@_inlineable // FIXME(sil-serialize-all)
public static var bitWidth: Int {
- return 2 * Base.bitWidth
+ return High.bitWidth + Low.bitWidth
}
% for (operator, name) in [('+', 'adding'), ('-', 'subtracting')]:
@@ -368,13 +320,14 @@ extension DoubleWidth: FixedWidthInteger {
return (0, self)
}
- // Calculate the number of bits before q and rhs line up,
- // we can skip that many bits of iteration.
+ // Calculate the number of bits before q and rhs line up; we can skip that
+ // many bits of iteration.
let initialOffset = q.leadingZeroBitCount +
(DoubleWidth.bitWidth - rhs.leadingZeroBitCount) - 1
// Start with remainder capturing the high bits of q.
- // (These need to be smart shifts, as initialOffset can be > q.bitWidth)
+ // (These need to be smart shifts, as initialOffset can be greater than
+ // q.bitWidth.)
var r = q >> Magnitude(DoubleWidth.bitWidth - initialOffset)
q <<= Magnitude(initialOffset)
@@ -392,7 +345,7 @@ extension DoubleWidth: FixedWidthInteger {
}
}
- // Sign of remainder matches dividend
+ // Sign of remainder matches dividend.
let remainder = self < (0 as DoubleWidth)
? 0 - DoubleWidth(r)
: DoubleWidth(r)
@@ -408,7 +361,7 @@ extension DoubleWidth: FixedWidthInteger {
public func dividedReportingOverflow(by other: DoubleWidth)
-> (partialValue: DoubleWidth, overflow: Bool) {
if other == (0 as DoubleWidth) ||
- (DoubleWidth.isSigned && other == (-1 as Int) && self == .min)
+ (DoubleWidth.isSigned && other == -1 && self == .min)
{
return (self, true)
}
@@ -419,12 +372,8 @@ extension DoubleWidth: FixedWidthInteger {
@_inlineable // FIXME(sil-serialize-all)
public func remainderReportingOverflow(dividingBy other: DoubleWidth)
-> (partialValue: DoubleWidth, overflow: Bool) {
- if other == 0 ||
- (DoubleWidth.isSigned && other == -1 && self == .min)
- {
- return (self, true)
- }
-
+ if other == (0 as DoubleWidth) { return (self, true) }
+ if DoubleWidth.isSigned && other == -1 && self == .min { return (0, true) }
return (quotientAndRemainder(dividingBy: other).remainder, false)
}
@@ -459,8 +408,7 @@ extension DoubleWidth: FixedWidthInteger {
let low = DoubleWidth((mid1.partial, a.partial))
let high = DoubleWidth((
- High(mid2.carry + d.carry), mid1.carry + mid2.partial
- ))
+ High(mid2.carry + d.carry), mid1.carry + mid2.partial))
if isNegative {
let (lowComplement, overflow) = (~low).addingReportingOverflow(1)
@@ -506,13 +454,7 @@ extension DoubleWidth: FixedWidthInteger {
lhs = 0
return
}
-
- // Shift is exactly the width of `Base`, so low -> high.
- if rhs._storage.low == Base.bitWidth {
- lhs = DoubleWidth((High(truncatingIfNeeded: lhs._storage.low), 0))
- return
- }
-
+
lhs &<<= rhs
}
@@ -530,47 +472,48 @@ extension DoubleWidth: FixedWidthInteger {
lhs = lhs < (0 as DoubleWidth) ? ~0 : 0
return
}
-
- // Shift is exactly the width of `Base`, so high -> low.
- if rhs._storage.low == Base.bitWidth {
- lhs = DoubleWidth((
- lhs < (0 as DoubleWidth) ? ~0 : 0,
- Low(truncatingIfNeeded: lhs._storage.high)
- ))
- return
- }
lhs &>>= rhs
}
@_inlineable // FIXME(sil-serialize-all)
public static func &<<=(lhs: inout DoubleWidth, rhs: DoubleWidth) {
- // Need to use smart shifts here, since rhs can be > Base.bitWidth
- let rhs = rhs & DoubleWidth(DoubleWidth.bitWidth - 1)
+ let rhs = rhs & DoubleWidth(DoubleWidth.bitWidth &- 1)
- lhs._storage.high <<= High(rhs._storage.low)
-
- let lowInHigh = Base.bitWidth > rhs._storage.low
- ? lhs._storage.low >> (numericCast(Base.bitWidth) - rhs._storage.low)
- : lhs._storage.low << (rhs._storage.low - numericCast(Base.bitWidth))
- lhs._storage.high |= High(truncatingIfNeeded: lowInHigh)
+ guard rhs._storage.low < Base.bitWidth else {
+ lhs._storage.high = High(
+ truncatingIfNeeded: lhs._storage.low &<<
+ (rhs._storage.low &- Low(Base.bitWidth)))
+ lhs._storage.low = 0
+ return
+ }
- lhs._storage.low <<= rhs._storage.low
+ guard rhs._storage.low != (0 as Low) else { return }
+ lhs._storage.high &<<= High(rhs._storage.low)
+ lhs._storage.high |= High(
+ truncatingIfNeeded: lhs._storage.low &>>
+ (Low(Base.bitWidth) &- rhs._storage.low))
+ lhs._storage.low &<<= rhs._storage.low
}
@_inlineable // FIXME(sil-serialize-all)
public static func &>>=(lhs: inout DoubleWidth, rhs: DoubleWidth) {
- // Need to use smart shifts here, since rhs can be > Base.bitWidth
- let rhs = rhs & DoubleWidth(DoubleWidth.bitWidth - 1)
-
- lhs._storage.low >>= rhs._storage.low
+ let rhs = rhs & DoubleWidth(DoubleWidth.bitWidth &- 1)
- let highInLow = Base.bitWidth > rhs._storage.low
- ? lhs._storage.high << (numericCast(Base.bitWidth) - rhs._storage.low)
- : lhs._storage.high >> (rhs._storage.low - numericCast(Base.bitWidth))
- lhs._storage.low |= Low(truncatingIfNeeded: highInLow)
+ guard rhs._storage.low < Base.bitWidth else {
+ lhs._storage.low = Low(
+ truncatingIfNeeded: lhs._storage.high &>>
+ High(rhs._storage.low &- Low(Base.bitWidth)))
+ lhs._storage.high = lhs._storage.high < (0 as High) ? ~0 : 0
+ return
+ }
- lhs._storage.high >>= High(truncatingIfNeeded: rhs._storage.low)
+ guard rhs._storage.low != (0 as Low) else { return }
+ lhs._storage.low &>>= rhs._storage.low
+ lhs._storage.low |= Low(
+ truncatingIfNeeded: lhs._storage.high &<<
+ High(Low(Base.bitWidth) &- rhs._storage.low))
+ lhs._storage.high &>>= High(rhs._storage.low)
}
%{
@@ -608,11 +551,9 @@ binaryOperators = [
@_inlineable // FIXME(sil-serialize-all)
public init(_truncatingBits bits: UInt) {
_storage.low = Low(_truncatingBits: bits)
- _storage.high = High(_truncatingBits: bits >> UInt(Base.bitWidth))
+ _storage.high = High(_truncatingBits: bits >> UInt(Low.bitWidth))
}
- // other
- //
@_inlineable // FIXME(sil-serialize-all)
public init(_builtinIntegerLiteral _x: _MaxBuiltinIntegerType) {
var _x = _x
@@ -652,22 +593,17 @@ binaryOperators = [
_precondition(!overflow, "Literal integer out of range for this type")
}
- @_inlineable // FIXME(sil-serialize-all)
- public var description: String {
- return "(\(_storage.high), \(_storage.low))"
- }
-
@_inlineable // FIXME(sil-serialize-all)
public var leadingZeroBitCount: Int {
return high == (0 as High)
- ? Base.bitWidth + low.leadingZeroBitCount
+ ? High.bitWidth + low.leadingZeroBitCount
: high.leadingZeroBitCount
}
@_inlineable // FIXME(sil-serialize-all)
public var trailingZeroBitCount: Int {
return low == (0 as Low)
- ? Base.bitWidth + high.trailingZeroBitCount
+ ? Low.bitWidth + high.trailingZeroBitCount
: low.trailingZeroBitCount
}
@@ -676,11 +612,6 @@ binaryOperators = [
return high.nonzeroBitCount + low.nonzeroBitCount
}
- @_inlineable // FIXME(sil-serialize-all)
- public var hashValue: Int {
- return _mixInt(high.hashValue) ^ low.hashValue
- }
-
@_inlineable // FIXME(sil-serialize-all)
@_transparent
public var byteSwapped: DoubleWidth {
diff --git a/stdlib/public/core/Integers.swift.gyb b/stdlib/public/core/Integers.swift.gyb
index dbfc95309e1f6..58d8172927c01 100644
--- a/stdlib/public/core/Integers.swift.gyb
+++ b/stdlib/public/core/Integers.swift.gyb
@@ -2367,7 +2367,7 @@ extension FixedWidthInteger {
) -> (value: Self?, exact: Bool) {
guard _fastPath(!source.isZero) else { return (0, true) }
guard _fastPath(source.isFinite) else { return (nil, false) }
- guard Self.isSigned || source > 0 else { return (nil, false) }
+ guard Self.isSigned || source > -1 else { return (nil, false) }
let exponent = source.exponent
if _slowPath(Self.bitWidth <= exponent) { return (nil, false) }
let minBitWidth = source.significandWidth
@@ -2405,7 +2405,7 @@ extension FixedWidthInteger {
/// zero, a runtime error may occur.
///
/// let z = UInt(-21.5)
- /// // Error: ...the result would be less than UInt.min
+ /// // Error: ...outside the representable range
///
/// - Parameter source: A floating-point value to convert to an integer.
/// `source` must be representable in this type after rounding toward
@@ -2417,8 +2417,7 @@ extension FixedWidthInteger {
guard let value = Self._convert(from: source).value else {
fatalError("""
\(T.self) value cannot be converted to \(Self.self) because it is \
- infinite or NaN, or because the result would be greater than \
- \(Self.self).max or less than \(Self.self).min
+ outside the representable range
""")
}
self = value
diff --git a/test/stdlib/DoubleWidth.swift b/test/stdlib/DoubleWidth.swift
index d6d874e1ffcbc..86b31fcdae29b 100644
--- a/test/stdlib/DoubleWidth.swift
+++ b/test/stdlib/DoubleWidth.swift
@@ -161,6 +161,21 @@ dwTests.test("inits") {
_ = DWU16(UInt32.max)
}
+dwTests.test("Magnitude") {
+ typealias DWU16 = DoubleWidth
+ typealias DWI16 = DoubleWidth
+
+ expectTrue(DWU16.min.magnitude == UInt16.min.magnitude)
+ expectTrue((42 as DWU16).magnitude == (42 as UInt16).magnitude)
+ expectTrue(DWU16.max.magnitude == UInt16.max.magnitude)
+
+ expectTrue(DWI16.min.magnitude == Int16.min.magnitude)
+ expectTrue((-42 as DWI16).magnitude == (-42 as Int16).magnitude)
+ expectTrue(DWI16().magnitude == Int16(0).magnitude) // See SR-6602.
+ expectTrue((42 as DWI16).magnitude == (42 as Int16).magnitude)
+ expectTrue(DWI16.max.magnitude == Int16.max.magnitude)
+}
+
dwTests.test("TwoWords") {
typealias DW = DoubleWidth
@@ -210,6 +225,14 @@ dwTests.test("Remainder/DividingBy0") {
_ = f(42, 0)
}
+dwTests.test("RemainderReportingOverflow/DividingByMinusOne") {
+ func f(_ x: Int256, _ y: Int256) -> Int256 {
+ return x.remainderReportingOverflow(dividingBy: y).partialValue
+ }
+ expectEqual(f(.max, -1), 0)
+ expectEqual(f(.min, -1), 0)
+}
+
dwTests.test("Division/By0") {
func f(_ x: Int1024, _ y: Int1024) -> Int1024 {
return x / y
diff --git a/test/stdlib/integer_conversions.swift b/test/stdlib/integer_conversions.swift
index f77c90694b36e..25dabd31508a0 100644
--- a/test/stdlib/integer_conversions.swift
+++ b/test/stdlib/integer_conversions.swift
@@ -91,6 +91,7 @@ print(Int8._convert(from: -Double.leastNormalMagnitude))
// CHECK: (value: Optional(0), exact: false)
print(UInt8._convert(from: -1))
+print(UInt8._convert(from: -0.9))
print(UInt8._convert(from: 255))
print(UInt8._convert(from: 256))
print(UInt8._convert(from: Double.infinity))
@@ -98,6 +99,7 @@ print(UInt8._convert(from: -Double.infinity))
print(UInt8._convert(from: Double.nan))
// CHECK: (value: nil, exact: false)
+// CHECK: (value: Optional(0), exact: false)
// CHECK: (value: Optional(255), exact: true)
// CHECK: (value: nil, exact: false)
// CHECK: (value: nil, exact: false)