3. Numbers & math¶
What & why¶
Arithmetic is easy to write and surprisingly easy to get wrong: integer division truncates, floating point is inexact, and overflow can crash. Swift's choices here favour loud failure over silent wrong answers.
Basic operators¶
let a = 17
let b = 5
print(a + b) // 22
print(a - b) // 12
print(a * b) // 85
print(a / b) // 3 ← integer division drops the remainder
print(a % b) // 2 ← remainder
With Doubles, / is real division:
Compound assignment: += -= *= /= %=. Swift has no ++ or --; write count += 1.
Remainder for floating point is truncatingRemainder(dividingBy:):
Operator precedence¶
Multiplication, division and remainder bind tighter than addition and subtraction. Use parentheses whenever it helps a reader:
Overflow traps — on purpose¶
An Int has a fixed range. Going outside it crashes instead of wrapping silently:
print(Int.max) // 9223372036854775807
print(Int.min)
// let boom = Int.max + 1 // fatal error: arithmetic operation overflowed
If you actually want wraparound (hashing, checksums), use the overflow operators &+ &- &*:
Or detect it:
let (result, didOverflow) = Int.max.addingReportingOverflow(1)
print(result, didOverflow) // -9223372036854775808 true
Division by zero for integers also traps. Check first.
Floating point is approximate¶
This is how binary floating point works in every language. Compare with a tolerance, or avoid == on computed doubles:
func nearlyEqual(_ x: Double, _ y: Double, tolerance: Double = 1e-9) -> Bool {
abs(x - y) <= tolerance
}
print(nearlyEqual(0.1 + 0.2, 0.3)) // true
For money, store integer cents (or use Decimal from Foundation).
Special values: Double.infinity, -Double.infinity, and Double.nan ("not a number"). NaN != NaN; test with .isNaN.
Math functions¶
Import Foundation for the usual math library (it's available on all platforms):
import Foundation
print(sqrt(16.0)) // 4.0
print(pow(2.0, 10.0)) // 1024.0
print(sin(Double.pi / 2)) // 1.0
print(log(M_E)) // 1.0 (natural log)
print(abs(-5), max(3, 9), min(3, 9)) // 5 9 3
Rounding:
let x = 2.5
print(x.rounded()) // 3.0 (to nearest, ties away from zero)
print(x.rounded(.down)) // 2.0 (floor)
print(x.rounded(.up)) // 3.0 (ceiling)
print(x.rounded(.towardZero)) // 2.0 (trunc)
print(x.rounded(.toNearestOrEven)) // 2.0 (banker's rounding)
print(Int(x.rounded())) // 3
abs, min, and max work on any integer or floating-point type without Foundation.
Converting numbers¶
let i = 7
let d = Double(i) / 2 // 3.5
let big: Int = 300
let small = UInt8(truncatingIfNeeded: big) // 44 — keeps the low bits
// let crash = UInt8(big) // fatal error: not representable
let safe = UInt8(exactly: big) // nil — fails gracefully
init(exactly:) returns an optional and is the safest way to convert when the value might not fit.
Random numbers¶
let dice = Int.random(in: 1...6)
let chance = Double.random(in: 0..<1)
let coin = Bool.random()
let colors = ["red", "green", "blue"]
print(colors.randomElement()!) // random element (optional – `!` is explained later)
print(colors.shuffled())
These use a secure, system-provided generator by default. For reproducible sequences in tests, you can supply your own RandomNumberGenerator.
Formatting numbers for display¶
import Foundation
let price = 1234.5678
print(price.formatted()) // 1,234.568 (locale dependent)
print(price.formatted(.number.precision(.fractionLength(2)))) // 1,234.57
print(String(format: "%.2f", price)) // 1234.57
print(0.256.formatted(.percent)) // 26%
String(format:) uses C-style format strings; .formatted(...) is the modern, locale-aware way.
Common mistakes¶
- Integer division surprise:
1 / 2is0. Convert one side toDouble. - Comparing doubles with
==after arithmetic. - Letting
Intoverflow unnoticed in a loop — it will crash, which is at least visible. Use&+only when you truly want wraparound. Int(someDouble)on NaN or huge values crashes. CheckisFinitefirst or useInt(exactly:).- Forgetting
import Foundationforsqrt,pow,sin, etc. (Linux/Windows especially).
Exercises¶
- Compute the average of
[3, 8, 5]as aDouble— without hard-coding the answer. - Write code that converts Celsius to Fahrenheit:
F = C * 9 / 5 + 32. Test with100and-40. Careful with integer division! - Print the result of
Int.max &+ 1and then, in separate runs,Int.max + 1. Compare behaviour. - Roll two dice 1,000 times and print how many times the sum was 7.
- Given a radius, print the area of a circle rounded to 2 decimal places.
- Write
isPrime(_ n: Int) -> Bool(you may use a loop from the next lesson) and test it on a few numbers.
Recap¶
/onInts truncates;%is the remainder; no++/--.Intoverflow and division by zero trap;&+ &- &*wrap deliberately.- Floating point is approximate — compare with a tolerance.
import Foundationforsqrt,pow, trig, etc.- Convert explicitly;
init(exactly:)is the safe form.
Next: Strings →