Concrete integer types, part 2
Operator precedence
In C, operators evolved over time, and their precedence is a product of that historical legacy. C++, Java, and other “C family” languages have generally preserved the relative precedence of operators found in C.
In Swift, operator precedence has been rationalized. The resulting precedence table is similar to that of Go. Consequently, an integer expression in Swift can appear to be similar to an expression in another language but evaluate quite differently:
var v = 42
v = v + (v >> 4) & 0x0F0F0F0F
// In Swift (and Go), `v` is equal to 44.
var v = 42
v = v + (v >> 4) & 0x0F0F0F0F
// In JavaScript, `v` is equal to 12.
The relative precedence of infix operators common to both C and Swift can be compared as follows:
C  Swift 

<< >> 

* / % 
* / % & 
+  
+  ^  
<< >> 

< <= > >= 
< <= > >= == != 
== != 

& 

^ 

 

&& 
&& 
 
 
= and other assignment operators 
= and other assignment operators 
Overflow behavior
Rust now takes a similar approach to handling integer overflow; therefore, Swift users may find a writeup about Rust’s design evolution to be useful.
As previously mentioned, Swift’s standard arithmetic operators trap on integer
overflow. Integer overflow checking is disabled in Ounchecked
mode, which is not recommended for general use.
A runtime error also occurs in case of overflow in methods such as:
abs(_:)
negate()
dividingFullWidth(_:)
quotientAndRemainder(dividingBy:)
At the time of writing, dividingFullWidth(_:)
does not behave as
documented in case of overflow.
Absolute value and magnitude
In Swift, abs(_:)
returns a value of the same type as the argument.
Specifically, the function returns the absolute value of the argument.
Therefore, for a signed (and fixedwidth) integer type T
, a runtime error
occurs when evaluating abs(T.min)
because the result cannot be represented in
T
.
By contrast, evaluating T.min.magnitude
does not cause a runtime error.
However, the value is not always of type T
but rather of the associated type
T.Magnitude
. For a signed type, T.Magnitude
is the unsigned type of the same
bit width; for an unsigned type, T.Magnitude == T
.
Overflow operators
In C, the result of an unsigned integer operation that is too large to be represented “wraps around” (that is, the return value consists of the least significant bits of the result), while signed integer overflow is undefined behavior.
Sometimes, “wrapping” behavior can be desired in Swift (for example, when performing bitwise manipulations). The Swift standard library offers alternative facilities that can be used in these circumstances.
Three overflow operators allow the user to choose Clike “wrapping”
behavior instead of trapping on overflow: &+
(overflow addition), &
(overflow subtraction), and &*
(overflow multiplication). The behavior of
these operations is fully defined for both unsigned and signed integer types.
In March 2018, the three corresponding overflow assignment operators (
&+=
,&=
, and&*=
) were added to Swift. They are, of course, “wrapping” counterparts to assignment operators spelled without a leading&
.
The “overflow” operators
&/
and&%
were removed in Swift 1.2 because they did not provide two’s complement behavior like other overflow operators.
Methods reporting overflow
Five methods reporting overflow are provided, largely analogous to Rust’s
overflowing_*
methods:
addingReportingOverflow(_:)
subtractingReportingOverflow(_:)
multipliedReportingOverflow(by:)
dividedReportingOverflow(by:)
remainderReportingOverflow(dividingBy:)
In general, these operations return a tuple of a numeric value and a Boolean value. The numeric value is either the entire result if no overflow occurred during the operation or the “wrapped” partial result if overflow occurred; the Boolean value indicates whether or not overflow occurred.
Some caveats:
In Swift, x.dividedReportingOverflow(by: 0)
is documented to return
(x, true)
. Nonetheless, at time of writing, a divisionbyzero error occurs if
the righthand side (RHS) is expressed as a literal 0
:
let x = 42
let y = 0
x.dividedReportingOverflow(by: y)
// (partialValue: 42, overflow: true)
x.dividedReportingOverflow(by: 0)
// error: division by zero
In Swift,
x.remainderReportingOverflow(dividingBy: 0)
returns (x, true)
, as the remainder is mathematically undefined. Otherwise,
if the operation overflows (which only occurs when dividing by 1
), the method
returns (0, true)
. Mathematically, of course, the remainder of division by −1
is always zero. At the time of writing, a divisionbyzero error occurs if the
RHS is expressed as a literal 0
.
Internally, there are no LLVM primitives for checking overflow after division, so checking is implemented in native Swift.
Prior to Swift 4.2,
remainderReportingOverflow(dividingBy:)
did not return the correct remainder when dividing by1
. The behavior was fixed in early 2018.
Unsafe methods
Four unsafe methods were once provided:
unsafeAdding(_:)
unsafeSubtracting(_:)
unsafeMultiplied(by:)
unsafeDivided(by:)
The behavior of those methods was undefined in case of overflow. Therefore, they were useful only for avoiding the performance cost of overflow checking, and they were meant to be used only if it was certain that the result would not overflow. (In debug mode, however, overflow did cause a precondition failure.)
All four unsafe methods have been removed for Swift 5, as they were never approved as part of a proposal.
Fullwidth methods
Two primitive operations are exposed by the Swift standard library that can be useful for implementation of an arbitrarywidth integer type:
multipliedFullWidth(by:)
returns a tuple of the high and low parts of a
product that overflows standard multiplication.
dividingFullWidth(_:)
returns the quotient and remainder after the argument (a
doublewidth value expressed as a tuple of high and low parts) is divided by the
receiver. As mentioned above, a runtime error may occur if the quotient is not
representable within the bounds of the type.
Notice that this method is named “dividing” instead of “divided by.” Here, the argument is the dividend (i.e., numerator). Although unique among Swift arithmetic operations, this arrangement is necessary because the dividend is a tuple; tuple types can’t themselves be extended in Swift.
At the time of writing, the implemented behavior of dividingFullWidth(_:)
in
case of overflow does not match the documented behavior.
Integer remainder
The remainder operator %
(known as the modulo operator in other languages)
adopts the same truncated division convention observed in many “C family”
languages, including C99, C++11, C#, D, Java, JavaScript, and Rust.
The result has the same sign as the dividend.
Evaluating x % 0
results in a divisionbyzero error.
Bitwise operations
Every integer value has the instance property bitWidth
, which is the number of
bits in the binary representation of the value. All standard library integer
types have a fixed bit width; fixedwidth integer types have a static property
bitWidth
which, unsurprisingly, is equal to the instance property bitWidth
for any value of that type.
In generic code, it can be useful to work with the bit width of a fixedwidth integer type without having to instantiate an instance of that type. A key overarching goal of Swift’s protocolbased designs is to enable useful generic algorithms; this is the reason why fixedwidth integers have an instance property and a static property that are equal in value.
The following properties of a value’s binary representation are also available in Swift:
 Count trailing zeros (ctz), the number of zero bits that follow the least
significant one bit in the value’s binary representation:
trailingZeroBitCount
.  Count leading zeros (clz), the number of zero bits that precede the most
significant one bit in the value’s binary representation:
leadingZeroBitCount
.  Population count, the number of one bits in the value’s binary
representation:
nonzeroBitCount
.
Swift 4+ now has two sets of bit shifting operators to avoid undefined behavior in the case of overshift or undershift:
 Smart shifts are spelled
<<
and>>
, with corresponding assignment operators.  Masking shifts are spelled
&<<
and&>>
, with corresponding assignment operators.
The result of a bit shift is always of the same type as the lefthand side (LHS). In Swift 4+, the type of the righthand side (RHS) does not need to match that of the LHS.
In C, Java, and Rust, bit shifting operators are leftassociative just like multiplication or addition operators. In Swift, bit shifting operators are nonassociative. This means that parentheses are always required when an operand is both preceded and followed by bit shifting operators:
let x = 1 << 2 << 3
// error: adjacent operators are in nonassociative precedence group
// 'BitwiseShiftPrecedence'
Smart shifts
As in Java, Go, and other languages, >>
is a right arithmetic
shift for signed integers. In other words, the result has the same
sign bit as the LHS.
Undershift occurs when the RHS is negative. A right smart shift by a
negative RHS value x
is equivalent to a left smart shift by x.magnitude
,
and vice versa. For example, x >> 42
is equivalent to x << 42
.
Overshift occurs when the RHS equals or exceeds the bit width of the LHS. A
left smart shift by such a value is equivalent to filling in each bit of the
result with zero; in other words, the result is 0
. A right smart shift by
such a value is equivalent to filling each bit of the result with the LHS sign
bit; in other words, the result is either 1
if the LHS is negative or 0
if
the LHS is nonnegative.
Masking shifts
Masking shifts, introduced in Swift 4, offer an alternative when branches for handling undershift and overshift in smart shifts cannot be optimized away by the compiler and are of concern to performance.
In Rust, the same operations are known as wrapping shifts.
To obtain the result, the RHS is preprocessed to ensure that it is in the range
0..<lhs.bitWidth
. For a LHS of arbitrary bit width, preprocessing the RHS
requires computing the modulus after floored division. In other
words, the amount by which to shift the LHS can be obtained as follows:
var shift = rhs % lhs.bitWidth
if shift < 0 { shift += lhs.bitWidth }
When the bit width of the LHS is a power of two (as is the case for all standard library types), the same result can be obtained using a bitwise operation:
var shift = rhs & (lhs.bitWidth  1)
On most architectures, masking is performed by the CPU’s shift instructions and therefore incurs no additional performance cost.
Masking shifts and smart shift semantics were introduced as part of the Swift Evolution proposal SE0104: Protocoloriented integers.
Previous:
Concrete integer types, part 1
Next:
Concrete binary floatingpoint types, part 1
27 February–10 March 2018
Updated 6 July 2019