# 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.

Last Updated: Jan 16, 2020

### 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
``````

### 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 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(truncatingIfNeeded: 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)
``````

### 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 = maxInt32.addingReportingOverflow(maxInt32)        // (-2, true)
let wrapSub = Int8(-127).subtractingReportingOverflow(2)        // (127, true)
let wrapMult = UInt8(128).multipliedReportingOverflow(by: 2)    // (0, true)
let remainder = 127.remainderReportingOverflow(dividingBy: 10)  // (7, false)
``````

These methods return a tuple with the result and a boolean flag that is true if the result overflowed.

At time of writing a compiler bug SR-5964 means that the `dividedReportingOverflow` may fail to compile when it detects an overflow will happen:

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

### Bitwise Operators

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         // 179 (0b1011_0011)
let xorByte = byte ^ mask        // 147 (0b1001_0011)
``````

Using the bit shifting operators `<<` and `>>` with unsigned integers discards bits shifted beyond the bounds, empty bits are filled with zeroes:

``````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
``````

When bit shifting signed integers to the right, empty bits on the left are filled with 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
``````