When you start implementing your own arithmetic algorithms you will sooner than later encounter overflow errors. Unlike arithmetic operators in C, arithmetic operator in Swift don’t overflow by default. Overflow behaviour is trapped and reported as an error.
You have two approaches to solve these overflow problems. Handle overflow in your own calculation logic. For this Swift provides it’s own second set of arithmetic operator that overflow by default (like in C), such as the overflow multiplication(%*) or addition operator (&+). Alle these overflow tolerant operators start with the ampersand letter.
The other approach is to use special arithmetic methods that always return a tuple where the second value of the tuple contains an overflow flag. You need to check this flag after each calculation…
We will demonstrate overflow handling with by solving the Revers Integer Problem with the second approach.
Given a signed 32-bit integer x, return x with its digits reversed. If reversing x causes the value to go outside the signed 32-bit integer range [-231, 231 – 1], then return 0.
Revers Integer Problem
Assume the environment does not allow you to store 64-bit integers (signed or unsigned).
Use bigger Integer Types
Let’s start with the easiest solution and use Int data type in Swift that contains a 64 bit numbers. The solution is straight forward using modulo and integer div operators – with module 10 you always get the last digit back.
func reverse(_ x: Int) -> Int {
guard Int32.min <= x && x <= Int32.max else {
return 0
}
var result: Int = 0
var remain = x
while remain != 0 {
result = result * 10 + remain % 10
remain = remain / 10
}
return Int32.min <= result && result <= Int32.max ? result : 0
}
The solution will always work when input number are 32 bit long – signed or unsigned. As we do all calculations with 64 bit numbers – Int is Int64 on 64 bit architectures – this approach we will NEVER produce an overflow!
Check for overflow
However we did not fulfil the assumption the environment does NOT allow to store 64 bit numbers. So we first transform Int to Int32. And we will get the following implementation:
func reverse(_ x: Int) -> Int {
var result: Int32 = 0
var remain = Int32(x)
while remain != 0 {
result = result * 10 + remain % 10
remain = remain / 10
}
return Int(result)
}
With this implementation we could get an Overflow error when using it i.e. when x = Int32.min
= -2147483648
we encounter overflow error as -8463847421
is not a Int32
.
let x = Int(Int32.min)
print(x)
print(reverse(x))
So let’s check for overflow after each arithmetic operation. Swift provides the overflow reporting functions multipliedReportingOverflow(by:)
and addingReportingOverflow(_)
. Using these functions we get the following implementation:
func reverse(_ x: Int) -> Int {
var remain = Int32(x)
var result: Int32 = 0
while remain != 0 {
var overflow = false
(result, overflow) = result.multipliedReportingOverflow(by:10)
if overflow { return 0 }
(result, overflow) = result.addingReportingOverflow(remain % 10)
if overflow { return 0 }
remain = remain / 10
}
return Int(result)
}