I'm currently working my way through the Swift language guide. Recently, I read the section about numbers and numeric types. Most of the stuff in this chapter should be familiar to developers who have been working in statically typed languages before. Some of the decisions of the language designers, though, are quite interesting and useful in my opinion. Here's what I found.
Swift currently supports ten numeric types. There are four types for signed integer types of different bit sizes and their unsigned equivalents, prefixed with
There's also an additional type named
Int which has the current platform's native word size. It corresponds to
Int32 on a 32-bit platform and to
Int64 on a 64-bit platform.
Finally, Swift offers two floating-point types which differ in size and in precision of their fractional component:
Float(size: 32-bit, precision: ≥6 decimal digits)
Double(size: 64-bit, precision: ≥15 decimal digits)
Integer Literals can be written in one of several representations:
- decimal, no prefix
- binary, prefixed with
- octal, prefixed with
- hexadecimal, prefixed with
The underlying value of the literal is independent of its representation. Therefore, the following four literals all have the same value:
let decimal = 42 // 42 = 4 * 10 + 2 * 1
let binary = 0b101010 // 42 = 1 * 32 + 1 * 8 + 1 * 2
let octal = 0o52 // 42 = 5 * 8 + 2 * 1
let hexadecimal = 0x2A // 42 = 2 * 16 + 10 * 1
Floating-point literals can only be decimal or hexadecimal. In comparison to many other programming languages, digits are required on both sides of the decimal point, thus making
.5 an invalid literal (which should've been written as
0.5 instead). In Swift, it's all about explicitness.
Formatting Numeric Literals
Swift allows you to insert arbitrary underscores in your numeric literals to improve the readability of long numbers by grouping digits together. You could initialize an integer constant holding the earth's population like this:
let earthPopulation = 7_100_000_000
The underscores are purely syntactic sugar and don't change the underlying value of the literal in any way.
Not surprisingly, numeric literals can be denoted using the scientific
e notation for decimal floats. You could write
7.1e9, meaning 7.1 times 10 to the 9th power.
There's also a notation for hexadecimal floats which uses the
p notation. The base is 2, not 10, so
0xAp3 means 10 times 2 to the 3rd power, which equals 80. Honestly, I've never come across that notation in my entire history of programming and I'd be careful to use it because it might lead to a lot of confusion. (It makes for great Star Wars anecdotes like
The Remainder Operator
Similar to other C-like languages, Swift supports the
% operator to calculate the remainder of a division. This operator is often referred to as modulo, which, strictly speaking, is incorrect in the mathematical sense. The value of
-5 mod 3 is
1, while Swift (and many other languages) return
-5 = (-1) * 3 - 2.
Another aspect that sets Swift apart is that its remainder operator also works on floating-point numbers on both operands:
let remainder = 2.5 % 1.1
The sign of the second operand never changes the value of the expression.
Numeric Type Conversions
In Swift, conversions between different numeric types have to be made explicit. The following addition is therefore not valid Swift code:
let one = 1
let oneHalf = 0.5
// Doesn't work:
let oneAndAHalf = one + oneHalf
You can solve this problem by initializing a new
Double from an existing
let oneAndAHalf = Double(one) + oneHalf
I think it's helpful to be clear about the (otherwise implicit) conversion that needs to happen when performing an addition on differently typed operands. This is another point where the Swift language designers favored clarity over brevity:
Because each numeric type can store a different range of values, you must opt in to numeric type conversion on a case-by-case basis. This opt-in approach prevents hidden conversion errors and helps make type conversion intentions explicit in your code.
To avoid errors caused by accidental overflow of numeric types, Swift doesn't allow values to overflow by default. Trying to perform such an invalid operation results in a runtime exception – fail hard, fail early. Examples of invalid operations include adding 1 to the maximum integer value or assigning a negative number to an unsigned variable.
If, for some reason, you do need arithmetic operations to deal with overflowing values, Swift provides five so-called overflow operators, all of which are prefixed with an ampersand sign:
- overflow addition:
- overflow subtraction:
- overflow multiplication:
The following two operators once existed, but were removed in Swift 1.2:
- overflow division:
- overflow remainder:
You'll find all of the nitty-gritty details about value overflow in the language guide under Advanced Operators // Overflow Operators // Value Overflow.