|
2 | 2 | // |
3 | 3 | // This source file is part of the Swift Numerics open source project |
4 | 4 | // |
5 | | -// Copyright (c) 2017-2021 Apple Inc. and the Swift Numerics project authors |
| 5 | +// Copyright (c) 2017-2024 Apple Inc. and the Swift Numerics project authors |
6 | 6 | // Licensed under Apache License v2.0 with Runtime Library Exception |
7 | 7 | // |
8 | 8 | // See https://swift.org/LICENSE.txt for license information |
@@ -281,17 +281,11 @@ extension DoubleWidth : FixedWidthInteger { |
281 | 281 | by rhs: DoubleWidth |
282 | 282 | ) -> (partialValue: DoubleWidth, overflow: Bool) { |
283 | 283 | let (carry, product) = multipliedFullWidth(by: rhs) |
284 | | - let result = DoubleWidth(truncatingIfNeeded: product) |
285 | | - |
286 | | - let isNegative = DoubleWidth.isSigned && |
287 | | - (self < (0 as DoubleWidth)) != (rhs < (0 as DoubleWidth)) |
288 | | - let didCarry = isNegative |
289 | | - ? carry != ~(0 as DoubleWidth) |
290 | | - : carry != (0 as DoubleWidth) |
291 | | - let hadPositiveOverflow = |
292 | | - DoubleWidth.isSigned && !isNegative && product.leadingZeroBitCount == 0 |
293 | | - |
294 | | - return (result, didCarry || hadPositiveOverflow) |
| 284 | + let partialValue = DoubleWidth(truncatingIfNeeded: product) |
| 285 | + // Overflow has occured if carry is not just the sign-extension of |
| 286 | + // partialValue (which is zero when Base is unsigned). |
| 287 | + let overflow = carry != (partialValue >> DoubleWidth.bitWidth) |
| 288 | + return (partialValue, overflow) |
295 | 289 | } |
296 | 290 |
|
297 | 291 | public func quotientAndRemainder( |
@@ -331,7 +325,11 @@ extension DoubleWidth : FixedWidthInteger { |
331 | 325 | if DoubleWidth.isSigned && other == -1 && self == .min { return (0, true) } |
332 | 326 | return (quotientAndRemainder(dividingBy: other).remainder, false) |
333 | 327 | } |
334 | | - |
| 328 | + |
| 329 | + // When using a pre-Swift 6.0 runtime, `&*` is not a protocol requirement of |
| 330 | + // FixedWidthInteger, which results in the default implementation of this |
| 331 | + // operation ending up recursively calling itself forever. In order to avoid |
| 332 | + // this, we keep the concrete implementation around. |
335 | 333 | public func multipliedFullWidth( |
336 | 334 | by other: DoubleWidth |
337 | 335 | ) -> (high: DoubleWidth, low: DoubleWidth.Magnitude) { |
@@ -453,18 +451,21 @@ extension DoubleWidth : FixedWidthInteger { |
453 | 451 |
|
454 | 452 | /// Returns this value "masked" by its bit width. |
455 | 453 | /// |
456 | | - /// "Masking" notionally involves repeatedly incrementing or decrementing this |
457 | | - /// value by `self.bitWidth` until the result is contained in the range |
458 | | - /// `0..<self.bitWidth`. |
| 454 | + /// "Masking" notionally involves repeatedly incrementing or decrementing |
| 455 | + /// this value by `self.bitWidth` until the result is contained in the |
| 456 | + /// range `0..<self.bitWidth`. |
459 | 457 | internal func _masked() -> DoubleWidth { |
460 | | - // FIXME(integers): test types with bit widths that aren't powers of 2 |
| 458 | + let bits = DoubleWidth(DoubleWidth.bitWidth) |
461 | 459 | if DoubleWidth.bitWidth.nonzeroBitCount == 1 { |
462 | | - return self & DoubleWidth(DoubleWidth.bitWidth &- 1) |
| 460 | + return self & (bits &- 1) |
463 | 461 | } |
464 | | - if DoubleWidth.isSigned && self._storage.high < (0 as High) { |
465 | | - return self % DoubleWidth(DoubleWidth.bitWidth) + self |
466 | | - } |
467 | | - return self % DoubleWidth(DoubleWidth.bitWidth) |
| 462 | + let reduced = self % bits |
| 463 | + // bitWidth is always positive, but the value being reduced might have |
| 464 | + // been negative, in which case reduced will also be negative. We need |
| 465 | + // the representative in [0, bitWidth), so conditionally add the count |
| 466 | + // to get the positive residue. |
| 467 | + if Base.isSigned && reduced < 0 { return reduced &+ bits } |
| 468 | + return reduced |
468 | 469 | } |
469 | 470 |
|
470 | 471 | public static func &<<=(lhs: inout DoubleWidth, rhs: DoubleWidth) { |
@@ -590,6 +591,31 @@ extension DoubleWidth : FixedWidthInteger { |
590 | 591 | precondition(!overflow, "Overflow in %=") |
591 | 592 | lhs = result |
592 | 593 | } |
| 594 | + |
| 595 | + public static func &+( |
| 596 | + lhs: DoubleWidth, rhs: DoubleWidth |
| 597 | + ) -> DoubleWidth { |
| 598 | + let (low, carry) = lhs.low.addingReportingOverflow(rhs.low) |
| 599 | + let high = lhs.high &+ rhs.high &+ (carry ? 1 : 0) |
| 600 | + return DoubleWidth(high, low) |
| 601 | + } |
| 602 | + |
| 603 | + public static func &-( |
| 604 | + lhs: DoubleWidth, rhs: DoubleWidth |
| 605 | + ) -> DoubleWidth { |
| 606 | + let (low, borrow) = lhs.low.subtractingReportingOverflow(rhs.low) |
| 607 | + let high = lhs.high &- rhs.high &- (borrow ? 1 : 0) |
| 608 | + return DoubleWidth(high, low) |
| 609 | + } |
| 610 | + |
| 611 | + public static func &*( |
| 612 | + lhs: DoubleWidth, rhs: DoubleWidth |
| 613 | + ) -> DoubleWidth { |
| 614 | + let p00 = lhs.low.multipliedFullWidth(by: rhs.low) |
| 615 | + let p10 = lhs.high &* Base(truncatingIfNeeded: rhs.low) |
| 616 | + let p01 = Base(truncatingIfNeeded: lhs.low) &* rhs.high |
| 617 | + return DoubleWidth(p10 &+ p01 &+ Base(truncatingIfNeeded: p00.high), p00.low) |
| 618 | + } |
593 | 619 |
|
594 | 620 | public init(_truncatingBits bits: UInt) { |
595 | 621 | _storage.low = Low(_truncatingBits: bits) |
|
0 commit comments