As you may know, Swift is a type-safe language and good understanding of the data types that are available is therefore critically important. In this post, we’re going to start looking at these in detail with a look at the numeric data types available in Swit.
Table of Contents
Integers represent whole numbers, numbers that have no fractional components like
Signed and Unsigned Integers
In Swift, the integers are primarily divided into two groups. They can either be signed (which include negative values, zero and positive values) or unsigned (which covers only zero and positive values).
In addition to being either signed or unsigned, in Swift each of these two groups also come in four different sizes; 8, 16, 32 and 64-bits versions.
In Swift, the names of the integer data types follow the same general scheme as is used in languages like C and as with all types in Swift, they always start with a capital letter.
For the unsigned types this initial capital letter is the letter
U (to indicate that they are unsigned). This is then followed by the word
Int (with a capital
I) to indicate that they are also part of the integer type family. In the case of the signed integer types the initial letter
U is omitted and they simply start with the word
Int. After the word
Int though, both the signed and unsigned integer types include a number to indicate how many bits of storage the particular type makes use of. This can be one of the aforementioned 8, 16, 32 or 64 to represent 8, 16, 32 or 64-bits respectively.
Putting all of this together then you can see that we have a whole range of integer types at our disposal. These include integer types such as
UInt8 (an unsigned integer type using 8 bits of storage),
Int16 (a signed integer, – notice it has no leading
U – using 16 bits of storage) or
UInt64 (an unsigned integer type using 64-bits of storage). The full list is below:
Now although these types are useful, there are a lot of times when we don’t actually care about the number of bits of storage our integers are using. For these situations we make use of another, more generic, integer type built into Swift called
Int type is the generic integer type of choice when writing Swift code and you should use it whenever possible. By using
Int consistently instead of the sized integer types we just looked at we can increase both the portability and readability of our code and can significantly reduce the need to convert between different data types.
Given it’s more generic nature, you may be wondering how much storage the
Int type actually uses to store its values and this is actually a good question. Under the hood the
Int type maps to “a type that is of the same size as the current platforms native word size, the smallest addressable unit of memory on the particular platform.”
Now that may be a bit of a mouthful but all it really means is that on a 64-bit platform the
Int type would be the equivalent size to an
Int64 and on a 32-bit platform, it would be equivalent to an
Int type therefore using at least 32-bits of storage, for most situations the type provides more than enough storage space for the integer values you’ll normally be working. If however, you have specific requirements such as working with data from external data sources, are worried about performance or memory usage or are trying to perform other optimisations, you have the option of falling back to one of the sized integers such as
In addition to the
Int type though, Swift also contains an unsigned equivalent called
In similar fashion to the
Int type, the
UInt also maps to the native word size on a given platform so on a 64-bit platform it is the same size as a
UInt64 and on a 32-bit platform it is the same size as a
Despite the fact that the
UInt type is available within Swift though, Apple recommends that where possible, you use
Int in preference to the
UInt, even if the values that you are storing are unsigned.
Now, at first, this may seem strange, after all, why use a signed type even when you know the values you’re going to be storing are unsigned? But remember what I said about being consistent. By always using a single integer type whenever possible, we make our code easier to read, easier to move between platforms and also make sure that the values in our constants and variables are immediately interoperable without the need for any sort of conversion. It also has the advantage that we can take make use of the inbuilt type inference mechanism built into Swift which infers integers to be of type
Int by default.
So, we’ve looked at the integer types, lets now take a look at their counterparts, the floating-point numbers.
Floating-point numbers are numbers that have a fractional component such as
101.123 and as such are usually written using a decimal point.
Due to the way they are stored, variables and constants that are defined as floating-point numbers can store a much wider range of values than integers using the same number of bits of storage including values that are not only smaller, but also much larger than integers of the equivalent size. They do this though by lose some level of precision.
For the most part this loss of precision is not a major problem, especially when you stack this up against the greater flexibility that these numbers provide but you need to be aware that in the case of floating point numbers it can lead to some strange results such as when trying to test two floating point values for equality or trying to store a values that cannot be represented exactly using binary.
Float and Double
In Swift, there are two types of floating-point number, both of which are signed. These are the
Float type which represents 32-bit floating point numbers with at least 6 decimal digits of precision and the
Double type which represents 64-bit floating point numbers at least 15 decimal digits of precision.
In similar fashion to the discussion we had about
UInt, in the interests of consistency, Apple recommend that we use the
Double type in preference to the
Float type in situations where both types are equally applicable.
Their reasoning is the same as we saw above (mainly readability, consistency and portability) and also ties into Swifts type inference mechanism, which in the case of floating-point numbers, infers values to be of type
Double by default.
Numeric Literal Values
So, we’ve looked at the two groups of numeric data types in Swift, the integer types and the floating-point types but how do we actually write values of these types in our code? This is where literal values come in.
Literals values are exactly as they sound, they are values that are literally written in our code verbatim and in Swift we have a number of options for how we can write them. Let’s look at writing integer literals first.
In the case of the integer values, the first form we can use to write integer values in is good old decimal.
Decimal is the form you’ll be most familiar with where values are represented in a base-10 format without any sort of prefix or extra notation. For example:
let decimalInteger = 42 // 42 = (4 * 10) + (2 * 1)
On top of decimal notation though, there are three other notations that we can use to write integer values in Swift; binary, octal and hexadecimal. Let’s look at each of them in turn.
Binary Integer Literals
When we write an integer value in binary notation in Swift, we’re representing the value in a base-2 notation and write it with a leading zero, followed by a lowercase
b (for binary) followed by the value written in base-2.
For example, if I wanted to initialise a constant with the decimal value 42 written in binary notation I would write:
let binaryInteger = 0b101010 // The equivalent of decimal 42. // 42 = (1 * 32) + (1 * 8) + (1 * 2)
Octal Integer Literals
The next option we have is octal notation. Octal notation represents values in a base-8 notation and to indicate the values is in octal notation we write a leading zero followed by a lowercase
o (for octal) followed by the value in base-8.
For example, if we wanted to initialise a constant with the octal equivalent of decimal 42 we would write:
let octalInteger = 0o52 // The equivalent of decimal 42. // 42 = (5 * 8) + (2 * 1)
Hexadecimal Integer Literals
The final choice we have is hexadecimal notation. When we write a literal value in hexadecimal form we prefix the value with a zero followed by a lowercase
x from hexadecimal) followed by the number in hexadecimal.
So if we wanted to declare a final constant, again with the equivalent of the decimal value 42, but this time written in hexadecimal we would write :
let hexadecimalInteger = 0x2A // The equivalent of decimal 42. // 42 = (2 * 16) + (10 * 1)
As with integer values, it is relatively common to write floating-point literals within our code and in the case of floating-point numbers we actually have slightly fewer choices when it comes to formats as we are limited to either decimal notation or hexadecimal notation.
Decimal Floating-Point Literals
The first notation, decimal, is the one you be most familiar with. As you know this format uses a decimal point to separate the integer part of the number from the fractional part. For example:
let decimalDouble = 3.14159 // Pi
One thing to note is that in Swift, you must always have a number on both sides of the decimal point so writing something like
.5 in Swift is invalid.
Hexadecimal Floating-Point Literals
In addition to decimal notation, we can also write floating point numbers in Swift using hexadecimal notation. To write floating point numbers in hexadecimal notation you prefix the number with a zero, followed by a lower-case
0x). When writing floating-point numbers in this way, both the whole number and fractional parts are both written in hexadecimal and are separated by a decimal point. For example, I could write
pi in the following format:
let hexadecimalDouble = 0x3.374F
Now, if you can remember back to school, when you were writing either very larger or very small numbers you may have used something called scientific notation.
Scientific notation is a kind of short hand, a more convenient way of writing floating point numbers using an optional exponent and given that I’ve mentioned it here, it won’t surprise you that we also have the option of using this notation in Swift.
To write decimal floating point literals in scientific notation we use an upper or lowercase letter
e to separate the base value from the exponent. The total value of the floating point number is then equivalent of multiplying the base value, (the part of the number before the
e), by 10 (in the case of decimal values) raised to the power of the exponent value (where the exponent value is the part of the number after the
So if I wrote, the following:
2.56e2 it would be the equivalent of writing
2.56 * 10^2 or
Similarly, if I wrote:
2.56e-2, (notice here that I used a negative exponent), it would be the equivalent of writing:
2.56 * 10^-2 or
In Swift, we can also write floating point numbers in hexadecimal format and still make use of scientific notation.
When writing in hexadecimal format, instead of using an
E to separate the base value from the exponent we , we use an upper or lowercase letter
p. When written in hexadecimal format, the literal value is equivalent to multiplying the base value (which is written in hexadecimal) by 2 raised to the power of the exponent (which is also written in hexadecimal).
So for example if we wrote the following floating point literal:
0xAp2 It would be the equivalent of writing:
10 * 2^2 or
40.0. Notice how the number is still prefixed with the
0x to indicate that is is a hexadecimal number.
Similarly if I wrote
0xAp-2 It would be the equivalent of writing:
10 * 2^-2 or
Formatting for Numeric Literal Values
In addition to the different syntaxes that we can use to write numeric literal values, Swift also allows us to some syntactic sugar that helps us make those values easier to read. Firstly, both integers and floating point numbers can be written with additional leading zeros e.g.:
let pi = 00003.14159
They an also be written with underscores between group of digits to help with readability:
let largeInteger = 3_000_000_000 let largeDouble = 2_345_678.910_111_213
In both cases, the additional syntactic sugar has no effect on the underlying value that is represented, it is simply ignored by the compiler.
Integer Value Limits
Now, as I mentioned earlier, integer types in Swift use a fixed number of bits in which to store their values. In doing so, the number of bits they use, provides a direct limit on the range of values that variables or constants of that type can store.
For example, the
Int8 type, a signed integer using 8 bits of storage can store values ranging from
127 whereas it’s unsigned equivalent (
UInt8) can store values that range from
0 through to
In both cases, if you attempt to store a value that does not fit within the range of values supported by the type, the compiler will indicate it as an error at the point you compile your code.
So given the fact that the integer values in Swift have a particular range of values that they can store, how do we find out how do we find out what this range of values is without doing some fancy binary maths?
Well, in Swift, built into each of the integer types are a couple of type properties that allow us to discover this information.
Value Ranges of Types
To find the smallest value a particular integer type can store we use the
min property and access it using dot notation.
Note: All the dot notation is, is using a dot or full stop to separate a item we want to know something about (in this case a type) from the information we want to know about it (in this case the minimum value it can store which is represented by the
For example if I wanted to access the minimum value that can be stored in say an
Int8 I would write:
Int8.min // Returns -127
We can also access the maximum value that can be stored in a particular type. You can do this using the
max property. For example:
Int8.max // Returns 128
In both these cases, the values returned from these properties are of the same type as the type whose properties we’ve accessed so in the examples above the values returned from the
max properties would both be of type
Int8. This allows us to easily use these returned values in calculations of that type without the need to convert them. With that said, though, the need to convert values between different types is not unusual in Swift, so we’ll take a look at that next.
Numeric Type Conversion
Converting Between Integer Types
As we saw just now, each of the integer value types in Swift have a different range of values that they can store and to convert a value from one data type to another Swift forces us to explicitly opt-in to the conversion on a case-by-case basis. By doing so, it helps avoid hidden conversion errors by making us indicate these conversions explicitly.
The mechanism for converting one type to another in Swift is simple. We simply create a new value of the desired type and initialise it with the existing value:
let daysInAYear : Int16 = 365 let daysInJanuary : Int8 = 31 let totalDays = daysInAYear + UInt16(daysInJanuary)
In this example, we initially create two constants, the first,
daysInAYear is of type
Int16 and the second, the
daysInJanuary is of type
UInt8. In the last line, we then create a new constant (
totalDays) by combining the values held in the two previous constants. To do this though, the values that we are combining have to be of the same time so we first have to convert the
daysInJanuary constant into an
Int16 to match the type of the
daysInAYear constant. To do this we create a new value of type
UInt16 using initialisation syntax passing in the value from the
daysInJanuary constant. When then combine the new value with the “daysInAYear
constant to create thetotalDays
constant which Swift infers to be of typeInt16`.
typeName(initialValue) we used in the example above is an example of using the default
UInt16 initializer and as you can see, we provided it with an initialisation value as part of that call (in this case the value held in the
daysInJanuary constant). Behind the scenes, the
UInt16 type has a number of different initialisers, each of which accepts an initialisation parameter of a different type. In this case we make use of the initialiser that accepts a
UInt8 parameter but there are others. It’s a similar story with the other numeric types in Swift. Each them has a specific set of initialisers, each tailored to accept initialisation parameters of specific types. This means that you can’t simply initialise numeric values with any old type. There are also some subtleties to these initialisations as well. For example, the
UInt8 type has an initialiser that accepts a
UInt16 parameter but if the value you provide doesn’t fit within the range of values supported by a
255), the compiler will, by default, give you an error.
let littleUInt16 = 120 let littleUInt8 : UInt8 = UInt8(littleUInt16) let bigUInt16 = 1440 let bitUInt8 : UInt8 = UInt8(bigUInt16) // Compiler error.
Converting Between Integer and Floating Point Types
In addition to being able to convert between different integer types, Swift also allows us to convert between integer and floating-point types as well. As with the integer types, any conversion must be explicitly stated though:
let startingRatio = 1 // Inferred as an Int let frationalRatio = 0.61803398875 // Inferred as a Double let goldenRatio = Double(startingRatio) + fractionalRatio // goldenRatio equals 1.61803398875 and is inferred to be of type Double.
As we saw with the integer example earlier, in order to be able to combine the two values, we must first ensure that they are all of the same type. To achieve this, we create a new
Double value using the value stored in the
startingRatio constant (in this case, we’re calling an initialiser on the
Double type that accepts an
Int as a parameter) and then add that to the existing
fractionalRatio constant. The result is the
goldenRatio constant and which is inferred to be of type
Double by Swift.
Converting Between Floating Point and Integer Types
In addition to being able to convert from integers to floating point numbers, we can also convert the other way, from floating point number to integers and as we’ve seen with all the conversions so far, we must explicitly opt-in to this. In the case of floating point to integer conversions, this is even more important than normal as when a floating point value is converted into an integer value in Swift, the fractional part of the floating point number is truncated:
let integerGoldenRatio = Int(goldenRatio) // integerGoldenRatio is equal to 1 and is inferred to be of type Int
This applies for both
Converting Numeric Literals
One thing to point out at this point is are the rules for converting numeric literals. When we include numeric literals in our code they aren’t actually typed until they are evaluated by the compiler. This means that any literal values you write in your code don’t have to be converted before they can be combined. For example, if we revisited our golden ratio example notice how in this example, we don’t need to convert the integer literal
1 before we combine it:
let otherGoldenRatio = 1 + fractionalRatio
So that about wraps it up for the numeric data types in Swift. As ever, if I’ve missed anything or you have an questions, get in touch.