3. Arithmetic
Computers were invented to calculate, and they’re very good at it. In this lesson you’ll make Tessel do sums for you, from simple additions to square roots, and you’ll see what happens when a calculation goes wrong.
In this lesson you’ll learn:
- the five arithmetic operators:
+,-,*,/and% - in which order a calculation is done, and how parentheses change it
- why
7 / 2is3, and how to get3.5 - how to convert between
IntandFloat - shortcuts like
+=for changing a variable - math functions such as
sqrt,pow,abs,min,maxandround - how to show a number with a fixed number of decimals
- which calculations stop your program with an error
The five operators
Section titled “The five operators”An operator is a symbol that does something with values. These five do arithmetic:
| Operator | Meaning | Example | Result |
|---|---|---|---|
+ | add | 7 + 2 | 9 |
- | subtract | 7 - 2 | 5 |
* | multiply | 7 * 2 | 14 |
/ | divide | 7 / 2 | 3 |
% | remainder | 7 % 2 | 1 |
fn main() { print(7 + 2) print(7 - 2) print(7 * 2) print(7 / 2) print(7 % 2)}951431Computers use * for “times” because × isn’t on most keyboards, and /
for “divided by”.
Two results may surprise you. 7 / 2 is 3, not 3.5; you’ll see why in a
moment. And % gives the remainder of a division: 7 divided by 2 is 3,
with 1 left over, so 7 % 2 is 1.
Of course, you can calculate with named values too, and store the result:
fn main() { let width = 8 let height = 5 let area = width * height print("The room is {area} square meters.")}The room is 40 square meters.What is the remainder good for?
Section titled “What is the remainder good for?”More than you might think. Division and remainder together split a number into parts. For example, to turn minutes into hours and minutes:
fn main() { let totalMinutes = 135 let hours = totalMinutes / 60 let minutes = totalMinutes % 60 print("{totalMinutes} minutes is {hours} h {minutes} min") print(10 % 2) print(11 % 2)}135 minutes is 2 h 15 min01The last two lines show another common use: a number is even when its
remainder after dividing by 2 is 0, and odd when it’s 1.
Order of operations
Section titled “Order of operations”What is 2 + 3 * 4? Tessel follows the same rule you learned in school:
multiplication and division come before addition and subtraction. So it’s
2 + 12, which is 14. To do the addition first, use parentheses:
fn main() { print(2 + 3 * 4) print((2 + 3) * 4) print(10 - 4 - 3) print(20 / 2 * 5)}1420350The rules:
- What’s in parentheses
( )is worked out first. - Then
*,/and%. - Then
+and-. - Operators at the same level go from left to right:
10 - 4 - 3is6 - 3, which is3.
Common mistake: forgetting parentheses
Section titled “Common mistake: forgetting parentheses”To find the average of two numbers, you add them and divide by 2. This looks right but isn’t:
fn main() { let a = 8 let b = 6 print(a + b / 2) print((a + b) / 2)}117The first line calculates 8 + 3, because / goes first. Tessel can’t warn
you about this: both are valid calculations, just different ones. When you’re
not sure about the order, add parentheses. They never hurt, and they make your
intention clear to the reader.
Whole numbers and decimal numbers
Section titled “Whole numbers and decimal numbers”Here’s the mystery of 7 / 2 solved. When you divide an Int by an Int,
the answer is an Int too, so the fraction is dropped. When you divide a
Float by a Float, you get a Float, with the fraction:
fn main() { let slices = 7 let people = 2 print(slices / people) print(7.0 / 2.0)}33.5Both are useful. If 7 pizza slices are shared by 2 people who only take whole
slices, each gets 3 (and 1 is left: 7 % 2). If you’re sharing 7 liters of
juice, each person gets 3.5.
Note that the fraction is simply cut off, not rounded: 19 / 10 is 1, not
2.
Common mistake: mixing Int and Float
Section titled “Common mistake: mixing Int and Float”Tessel never mixes an Int and a Float in a calculation:
fn main() { let price = 2.5 let count = 4 let total = price * count print(total)}error: can't use `*` on `Float` and `Int` --> math.tsl:4:17 |4 | let total = price * count | ^^^^^^^^^^^^^ | = help: convert one side with `Float(…)` or `Int(…)`
1 error foundThis may feel strict. The reason is that mixing them silently is a classic
source of bugs: should the answer be whole or not? Should the fraction of
price be dropped? Tessel makes you decide.
Converting between Int and Float
Section titled “Converting between Int and Float”To decide, convert one of the values. Float(x) turns a number into a
Float, and Int(x) turns it into an Int:
fn main() { let price = 2.5 let count = 4 let total = price * Float(count) print(total) print(Int(total))}10.010This fixes the pizza problem too:
fn main() { let slices = 7 let people = 2 let each = Float(slices) / Float(people) print("Everyone gets {each} slices.") print(Int(3.99)) print(Int(-3.99))}Everyone gets 3.5 slices.3-3Int(…) drops the fraction, just like Int division: Int(3.99) is 3, not
4. To round to the nearest whole number instead, see round below.
A number you type directly into the code (a literal) is more flexible
than a variable. Where a Float is needed, 4 counts as 4.0, so
price * 4 works without any conversion. It’s only variables (and other
calculated values) that never change type by themselves.
Changing a variable: += and friends
Section titled “Changing a variable: += and friends”Changing a variable based on its own value is so common that there’s a short way to write it:
| Short | Means |
|---|---|
x += 5 | x = x + 5 |
x -= 5 | x = x - 5 |
x *= 5 | x = x * 5 |
x /= 5 | x = x / 5 |
fn main() { var coins = 10 coins += 5 print(coins) coins -= 3 print(coins) coins *= 2 print(coins) coins /= 4 print(coins)}1512246There’s no %= in Tessel. If you try it, Tessel tells you what to write
instead:
fn main() { var x = 17 x %= 5 print(x)}error: Tessel has no `%=` --> math.tsl:3:7 |3 | x %= 5 | ^^ | = help: write it out: `x = x % 5`
1 error foundMath functions
Section titled “Math functions”For more than the basic operators, Tessel has built-in functions. A
function is a named piece of work: you give it values between parentheses,
and it gives you back a result. You’ve already used one: print.
| Function | Gives back | Example | Result |
|---|---|---|---|
sqrt(x) | the square root of x | sqrt(16.0) | 4.0 |
pow(x, y) | x to the power y | pow(2.0, 10.0) | 1024.0 |
abs(x) | x without its minus sign | abs(-7) | 7 |
min(a, b) | the smaller of the two | min(3, 8) | 3 |
max(a, b) | the larger of the two | max(3, 8) | 8 |
round(x) | x rounded to the nearest whole number | round(2.5) | 3.0 |
fn main() { let a = 3.0 let b = 4.0 let c = sqrt(a * a + b * b) print("The long side is {c}.") let radius = 2.0 let area = pi * pow(radius, 2.0) print("The circle's area is {area}.")}The long side is 5.0.The circle's area is 12.566370614359172.pi is a built-in constant: 3.141592653589793.
A few rules to remember:
sqrt,powandroundwork withFloats and give back aFloat.round(2.6)is3.0. To get anInt, convert it:Int(round(2.6))is3.abs,minandmaxwork with either type, and give back the same type. Both values ofminandmaxmust have the same type.- To use an
Intvariable withsqrtorpow, convert it first:
fn main() { let n = 16 print(sqrt(n))}error: expected `Float`, found `Int` --> math.tsl:3:16 |3 | print(sqrt(n)) | ^ this is `Int` | = help: convert it with `Float(…)`
1 error foundWrite sqrt(Float(n)) and it prints 4.0.
There are more, such as floor, ceil, sin and cos. See
Math in the reference for the full list.
Showing a neat number
Section titled “Showing a neat number”A Float is printed with all its digits, which is often more than you want:
fn main() { let third = 1.0 / 3.0 print(third) print(third.formatted(decimals: 2)) print(third.formatted(decimals: 5)) let price = 4.5 print("Price: {price.formatted(decimals: 2)} coins") let votes = 7 print(Float(votes).formatted(decimals: 1))}0.33333333333333330.330.33333Price: 4.50 coins7.0third.formatted(decimals: 2) turns the number into text with exactly 2
digits after the point, rounded. The dot after third means “use something
that belongs to this value”: formatted is a function that every Float
has. decimals: is a label that says what the 2 is for. You’ll see
labels like this often.
formatted belongs to Float only. For an Int, convert first, as in the
last line. If you forget, Tessel reminds you:
error: `Int` has no method `formatted`; it's a `Float` method, with the
help convert it first: `Float(value).formatted(…)` . See
formatted for details.
Why so many digits in the first place? Computers store most decimal numbers
very slightly inaccurately, because they work in binary. For example,
print(0.1 + 0.2) shows 0.30000000000000004. It’s not a Tessel bug; nearly
every programming language does this. formatted hides that noise when you
show a number to a person.
When arithmetic goes wrong
Section titled “When arithmetic goes wrong”Some calculations have no sensible answer. When a program meets one while it is running, it stops with a runtime error: an error that happens while the program runs, not when it’s compiled. Tessel can’t catch these in advance, because they depend on the values at that moment.
Dividing by zero
Section titled “Dividing by zero”What is 12 cookies shared among 0 friends? There’s no answer, so a program that tries stops straight away:
fn main() { let cookies = 12 let friends = 0 print("Sharing {cookies} cookies...") print(cookies / friends) print("Done!")}Sharing 12 cookies...error: division by zero --> math.tsl:5:11The first line was printed, because it ran before the problem. Then the
program stopped at line 5, column 11, and "Done!" was never printed. The same
happens with % and zero.
With Floats, the rules are different: 12.0 / 0.0 gives inf (infinity)
and the program carries on.
Numbers that are too big
Section titled “Numbers that are too big”An Int can hold numbers up to 9,223,372,036,854,775,807 (about nine
quintillion). That’s big, but not infinite. A result that doesn’t fit is
called an overflow:
fn main() { var big = 9_000_000_000_000_000_000 print(big) big = big * 2 print(big)}9000000000000000000error: integer overflow --> math.tsl:4:11Some languages quietly give you a wrong answer in this situation (often a large negative number). Tessel stops instead, because a wrong answer that looks right is much more dangerous than a program that clearly stops.
In a later lesson, you’ll learn how to check a value before using it, so you can avoid these errors: for example, only divide when the number of friends isn’t zero.
Exercises
Section titled “Exercises”1. Seconds. Using constants for the seconds in a minute, the minutes in an hour and the hours in a day, print how many seconds there are in a day and in a week.
Solution
fn main() { let secondsPerMinute = 60 let minutesPerHour = 60 let hoursPerDay = 24 let secondsPerDay = secondsPerMinute * minutesPerHour * hoursPerDay print("A day has {secondsPerDay} seconds.") print("A week has {secondsPerDay * 7} seconds.")}A day has 86400 seconds.A week has 604800 seconds.2. Temperature. To turn degrees Celsius into Fahrenheit, multiply by 9, divide by 5 and add 32. Convert 21.5 °C.
Solution
fn main() { let celsius = 21.5 let fahrenheit = celsius * 9 / 5 + 32 print("{celsius} °C is {fahrenheit} °F")}21.5 °C is 70.7 °Fcelsius is a Float, and the literals 9, 5 and 32 count as Floats
next to it. If celsius were the Int 21, the division would drop the
fraction, and 21 °C would come out as 69 °F instead of 69.8 °F.
3. Clock time. Turn 4000 seconds into hours, minutes and seconds, and
print 4000 seconds is 1 h 6 min 40 s. Use / and %.
Solution
fn main() { let totalSeconds = 4000 let hours = totalSeconds / 3600 let minutes = totalSeconds % 3600 / 60 let seconds = totalSeconds % 60 print("{totalSeconds} seconds is {hours} h {minutes} min {seconds} s")}4000 seconds is 1 h 6 min 40 sAn hour has 3600 seconds. totalSeconds % 3600 is what’s left after the
whole hours (400 seconds), and dividing that by 60 gives the whole minutes.
% and / are at the same level, so they go left to right.
4. Test average. You scored 82, 91 and 77 on three tests (store them as
Ints). Print the total, the average with one decimal, and the average
rounded to a whole number.
Solution
fn main() { let first = 82 let second = 91 let third = 77 let total = first + second + third let average = Float(total) / 3 print("Total: {total}") print("Average: {average.formatted(decimals: 1)}") print("Rounded: {Int(round(average))}")}Total: 250Average: 83.3Rounded: 83Without Float(…), total / 3 would be the Int 83, and the .3 would
be lost.
5. Splitting the bill. Four friends have dinner. The bill is 87.40, and
they want to add a 15% tip (store the percentage as the Int 15). Print the
tip, the total and how much each person pays, all with two decimals.
Solution
fn main() { let bill = 87.40 let tipPercent = 15 let people = 4 let tip = bill * Float(tipPercent) / 100 let total = bill + tip let each = total / Float(people) print("Tip: {tip.formatted(decimals: 2)}") print("Total: {total.formatted(decimals: 2)}") print("Each of {people} people pays {each.formatted(decimals: 2)}")}Tip: 13.11Total: 100.51Each of 4 people pays 25.13Summary
Section titled “Summary”+,-,*,/and%add, subtract, multiply, divide and give the remainder.*,/and%go before+and-. Use parentheses to change the order, or just to make it clear.Int / Intgives anIntand drops the fraction. For a decimal answer, divideFloats.- Tessel never mixes
IntandFloat. Convert withFloat(x)orInt(x)(Intdrops the fraction). x += 5is short forx = x + 5; there are also-=,*=and/=.sqrt,pow,abs,min,maxandroundare built-in math functions.x.formatted(decimals: 2)shows aFloatwith exactly two decimals.- Dividing an
Intby zero, or anIntresult that’s too big, stops the program with a runtime error.
See also: Operators and Converting between types.
Next: 4. Making decisions