Numbers and Numeric Types in Swift
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.
#Numeric Types
Swift currently supports ten numeric types. There are four types for signed integer types of different bit sizes and their unsigned equivalents, prefixed with U
:
Int8
andUInt8
Int16
andUInt16
Int32
andUInt32
Int64
andUInt64
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)
#Numeric Literals
Integer Literals can be written in one of several representations:
- decimal, no prefix
- binary, prefixed with
0b
- octal, prefixed with
0o
- hexadecimal, prefixed with
0x
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.
#Scientific Notation
Not surprisingly, numeric literals can be denoted using the scientific e
notation for decimal floats. You could write 7100000000
as 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 0xC3p0
, though.)
#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 -2
because -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
// 0.3
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 Int
:
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.
#Explicit Overflows
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.