Swift Integer Quick Guide

If you are new to Swift and have some experience with “C” style languages you probably have not given the Swift integer types much thought. They mostly work as you expect until one day something catches you out.

There were a number of operators such as the overflow operators &+, &- and &*, dealing with exact bit patterns and failable numeric intializers that were new to me. So here is my quick guide to Swift integers.

Integer Types

Let’s get the basics out of the way. Swift has the familiar C-style set of types for signed and unsigned types:

Int8, UInt8    // 8-bit
Int16, UInt16  // 16-bit
Int32, UInt32  // 32-bit
Int64, UInt64  // 64-bit
Int, UInt      // Platform dependent (64-bit on modern devices)

Unless you have a reason to care use Int.

let someInt = 42           // Int
let int8 = Int8(127)       // 8-bit signed integer
let unit8 = UInt8(255)     // 8-bit unsigned integer

You can get the min and max value of each type:

Int8.min       // -128
Int8.max       //  127
Int32.min      // -2147483648
Int32.max      //  2147483647
UInt64.min     //  0
UInt64.max     //  18446744073709551615

Integer Literals

You are not restricted to decimal for integer literals. You also have binary (0b), octal (0o) and hexadecimal (0x):

let decimal = 42        // 42
let binary  = 0b101010  // 42
let octal   = 0o52      // 42
let hex     = 0x2A      // 42

Add leading zeroes and _ separators to improve readability:

let hex2bytes = 0x00_ff
let downloads = 1_000_000_000

Initialization

The usual initializers for Swift types:

let int8 = Int8(127)       // 8-bit signed integer
let unit8 = UInt8(255)     // 8-bit unsigned integer

Be careful, initializing with a value too large to fit is an error:

let tooBig = Int8(128)     // Integer overflow

Initializing with a String can also fail so returns an Optional:

let zip = Int("95014")             // 95014
let badzip = Int("XYZ 30")         // nil
let rad16 = Int("FF", radix: 16)   // 255
let bitrot = Int8("020", radix: 2) // nil

You truncate a Float or Double when converting to an integer

let pi = Int(3.14)   // 3

Creating an integer from a big endian byte order (long time since I needed that):

let fromBigEnd = Int.init(bigEndian: 0x4000_0000_0000_0000) // 0x40
let fromLittleEnd = Int.init(littleEndian: 0x40)            // 0x40

Swapping bytes:

let aa:UInt16 = 0x00AA
let swapped = aa.byteSwapped // 0xAA00

If you need to maintain an exact bit pattern when converting between signed and unsigned integers:

let bits: UInt8 = 0b1000_0000        //  128
// let topBit = Int8(bits)           //  error - overflows
let topBit = Int8(bitPattern: bits)  // -128 = 0b10000000
let back = UInt8(bitPattern: topBit) //  128 = 0b10000000

To truncate a bit pattern keeping the least significant bits that fit:

let full16 = 0xAA05
let lower8 = UInt8(truncatingBitPattern: full16) // 5

Conversion Between Types

Using different number types for an operation gives an error. You must convert to a single type:

let height = Int8(5)
let width = 10
let area = height * width       // error Int8 * Int
let area = Int(height) * width  // 50 Int * Int

Trying to convert to a type with a value that does not fit gives an error:

let h = UInt8(25)     // UInt8(25)
let x = 10 * h        // UInt8(250)
// let y = 100 * h    // error - too big for UInt8
let y = 100 * Int(h)  // Int(2500)

IntMax is a typealias for the largest native signed integer:

let bigInt: IntMax = 1              // Int64 on 64-bit platform
let singleByte = Int8(15)           // Int8(15)
let maxSize = singleByte.toIntMax() // IntMax(15)

Failable Initializers

It is an error to initialize an integer type with a value that does not fit:

let tooBig = Int8(128)  // Error, Int8.max is 127

To avoid this type of error, Swift 3.1 introduced failable numeric initializers that return an optional:

let input = 128
let tooBig = Int8(exactly: input)  // nil
let fits = Int16(exactly: input)   // 128

Overflow Operators

Swift treats integer overflow or underflow as an error when using the standard operators.

let maxInt32 = Int32.max  // 2147483647
let minInt32 = Int32.min  // -2147483648

let moreThan32 = maxInt32 + 1  // error
let lessThan32 = minInt32 - 1  // error
let twiceAsBig = maxInt32 * 2  // error

To remove the error and have integers wrap use the overflow operators (&+, &-, &*):

let overflowAdd = maxInt32 &+ 1  // -2147483648
let overflowSub = minInt32 &- 1  //  2147483647
let overflowMult = maxInt32 &* 2 // -2

If you want to know when an overflow happens:

let wrapAdd = Int32.addWithOverflow(maxInt32, maxInt32) // (-2, true)
let wrapSub = Int8.subtractWithOverflow(-127, 2)        // (127, true)
let wrapMult = UInt8.multiplyWithOverflow(128, 2)       // (0, true)
let wrapDiv = Int32.divideWithOverflow(minInt32, -1)    // (0, true)
let remainder = Int8.remainderWithOverflow(127, 10)     // (7, false)

The result of the xxxWithOverflow method is a tuple with the result and a boolean flag that is true if the result overflowed.

At time of writing there is what seems to be a bug:

// https://bugs.swift.org/browse/SR-3535
let bug = Int.divideWithOverflow(Int.min, -1) // error

Bitwise Operations

Swift follows the C-style of bitwise operators for NOT (~), AND (&), OR (|), XOR (^):

let byte:UInt8 = 0b1010_0000     // 160 (0b1010_0000)
let mask:UInt8 = 0b0011_0011     //  51 (0b0011_0011)

let notByte = ~byte              //  95 (0b0101_1111)
let andByte = byte & mask        //  32 (0b0010_0000)
let orByte = byte | mask         // 168 (0b1011_0011)
let xorByte = byte ^ mask        // 147 (0b1001_0011)

The left bit shift << and right bit shift >> operators with unsigned integers:

For example:

 let pattern:UInt8 = 0b1100_0011
 pattern << 2     // 0b0000_1100
 pattern >> 2     // 0b0011_0000

You can rotate an unsigned integer by OR’ing the results of a left shift and right shift:

// 8-bits rotated by 2 bits
let rotateLeft = pattern << 2 | pattern >> 6  // 0b0000_1111
let rotateRight = pattern << 6 | pattern >> 2 // 0b1111_0000

For signed integers remember the extra rule for the sign bit:

For example with an 8-bit integer the leading bit is the sign bit leaving 7-bits for the value. You store negative values as 2’s complement which means subtracting the value from 2^7 (128) and setting the sign bit:

// -10 => 128 - 10 = 118 = 0b111_0110 
let minus10 = Int8(bitPattern: 0b1_111_0110) // -10
minus10 << 2 // -40 => 128 - 40 = 88 = 0b101_1000
minus10 >> 2 // -3  => 128 - 3 = 125 = 0b111_1101

let minus40 = Int8(bitPattern: 0b1_101_1000) // -40
let minus3  = Int8(bitPattern: 0b1_111_1101) // -3

Further Reading

A gist with the code snippets from this post:

For a preview of what might be coming in future Swift releases:

Never miss a post!

iOS Size Classes Cheat Sheet

Subscribe and get my free iOS Size Classes Cheat Sheet

Success! Now check your email to confirm your subscription and download your free guide to iOS Size Classes.

There was an error submitting your subscription. Please try again.

Unsubscribe at any time.
No time to watch WWDC videos?

Sign up to get my iOS posts direct to your inbox and I will send you a free PDF of my iOS Size Classes Cheat Sheet.

OK! Check your inbox (or spam folder) for an email to confirm your details and download your free guide to iOS Size Classes.

There was an error submitting your subscription. Please try again.

Unsubscribe at any time.
Archives Categories