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)