10
10
//===----------------------------------------------------------------------===//
11
11
12
12
extension FixedWidthInteger {
13
- @_transparent @usableFromInline
14
- var sextOrZext : Self { self >> Self . bitWidth }
13
+ /// `~0` (all-ones) if this value is negative, otherwise `0`.
14
+ ///
15
+ /// Note that if `Self` is unsigned, this always returns `0`,
16
+ /// but it is useful for writing algorithms that are generic over
17
+ /// signed and unsigned integers.
18
+ @inline ( __always) @usableFromInline
19
+ var signbit : Self {
20
+ return self < . zero ? ~ . zero : . zero
21
+ }
15
22
16
23
/// Saturating integer addition
17
24
///
@@ -29,8 +36,7 @@ extension FixedWidthInteger {
29
36
@inlinable
30
37
public func addingWithSaturation( _ other: Self ) -> Self {
31
38
let ( wrapped, overflow) = addingReportingOverflow ( other)
32
- if !overflow { return wrapped }
33
- return Self . max &- sextOrZext
39
+ return overflow ? Self . max &- signbit : wrapped
34
40
}
35
41
36
42
/// Saturating integer subtraction
@@ -54,7 +60,7 @@ extension FixedWidthInteger {
54
60
public func subtractingWithSaturation( _ other: Self ) -> Self {
55
61
let ( wrapped, overflow) = subtractingReportingOverflow ( other)
56
62
if !overflow { return wrapped }
57
- return Self . isSigned ? Self . max &- sextOrZext : 0
63
+ return Self . isSigned ? Self . max &- signbit : 0
58
64
}
59
65
60
66
/// Saturating integer negation
@@ -85,10 +91,10 @@ extension FixedWidthInteger {
85
91
public func multipliedWithSaturation( by other: Self ) -> Self {
86
92
let ( high, low) = multipliedFullWidth ( by: other)
87
93
let wrapped = Self ( truncatingIfNeeded: low)
88
- if high == wrapped. sextOrZext { return wrapped }
89
- return Self . max &- high. sextOrZext
94
+ if high == wrapped. signbit { return wrapped }
95
+ return Self . max &- high. signbit
90
96
}
91
-
97
+
92
98
/// Bitwise left with rounding and saturation.
93
99
///
94
100
/// `self` multiplied by the rational number 2^(`count`), saturated to the
@@ -102,28 +108,20 @@ extension FixedWidthInteger {
102
108
/// and if negative a right shift.
103
109
/// - rounding rule: the direction in which to round if `count` is negative.
104
110
@inlinable
105
- public func shiftedWithSaturation< Count: BinaryInteger > (
106
- leftBy count: Count , rounding rule: RoundingRule = . down
111
+ public func shiftedWithSaturation(
112
+ leftBy count: Int ,
113
+ rounding rule: RoundingRule = . down
107
114
) -> Self {
108
- // If count is zero or negative, negate it and do a right
109
- // shift without saturation instead, since we already have
110
- // that implemented.
115
+ if count == 0 { return self }
116
+ // If count is negative, negate it and do a right shift without
117
+ // saturation instead, since we already have that implemented.
111
118
guard count > 0 else {
112
- // negating count is tricky, because count's type can be
113
- // an arbitrary BinaryInteger; in particular, it could be
114
- // .min of a signed type, so that its negation cannot be
115
- // represented in the same type. Fortunately, Int64 is
116
- // always big enough to represent arbitrary shifts of
117
- // arbitrary types, so we can use that as an intermediate
118
- // type, and then we can use negatedWithSaturation() to
119
- // handle the .min case.
120
- let int64Count = Int64 ( clamping: count)
121
119
return shifted (
122
- rightBy: int64Count . negatedWithSaturation ( ) ,
120
+ rightBy: count . negatedWithSaturation ( ) ,
123
121
rounding: rule
124
122
)
125
123
}
126
- let clamped = Self . max &- sextOrZext
124
+ let clamped = Self . max &- signbit
127
125
guard count < Self . bitWidth else {
128
126
// If count is bitWidth or greater, we always overflow
129
127
// unless self is zero.
@@ -143,7 +141,27 @@ extension FixedWidthInteger {
143
141
// does equal 0b0000_0000.
144
142
let valueBits = Self . bitWidth &- ( Self . isSigned ? 1 : 0 )
145
143
let wrapped = self &<< count
146
- let complement = valueBits &- Int ( count)
147
- return self &>> complement == sextOrZext ? wrapped : clamped
144
+ let complement = valueBits &- count
145
+ return self &>> complement == signbit ? wrapped : clamped
146
+ }
147
+
148
+ /// Bitwise left with rounding and saturation.
149
+ ///
150
+ /// `self` multiplied by the rational number 2^(`count`), saturated to the
151
+ /// range `Self.min ... Self.max`, and rounded according to `rule`.
152
+ ///
153
+ /// See `shifted(rightBy:rounding:)` for more discussion of rounding
154
+ /// shifts with examples.
155
+ ///
156
+ /// - Parameters:
157
+ /// - leftBy count: the number of bits to shift by. If positive, this is a left-shift,
158
+ /// and if negative a right shift.
159
+ /// - rounding rule: the direction in which to round if `count` is negative.
160
+ @_transparent
161
+ public func shiftedWithSaturation(
162
+ leftBy count: some BinaryInteger ,
163
+ rounding rule: RoundingRule = . down
164
+ ) -> Self {
165
+ self . shiftedWithSaturation ( leftBy: Int ( clamping: count) , rounding: rule)
148
166
}
149
167
}
0 commit comments