13. Numbers and math
Go numbers come in two main kinds, do the usual arithmetic, and plug into standard library packages for more helpers. This chapter covers what you need for game logic: rounding, random numbers, basic maths, and moving between the two kinds.
Integers and floats
Go has two common number types:
- int. A whole number with no decimal part:
0,7,-42. - float64. A number with a decimal point:
3.14,0.5,-1.0.
In Go you do have to care which is which. 7 is an int,
7.0 is a float64, and the two kinds do not mix in
variables, as you will see below.
%T tells you which you have:
fmt.Printf("%T\n", 7) // int
fmt.Printf("%T\n", 7.0) // float64
fmt.Printf("%T\n", 3.14) // float64Arithmetic
The familiar operators are all here:
| Operator | Name | Example | Result |
|---|---|---|---|
+ |
addition | 3 + 2 |
5 |
- |
subtraction | 7 - 4 |
3 |
* |
multiplication | 6 * 7 |
42 |
/ |
division, whole numbers | 7 / 2 |
3 |
/ |
division, decimals | 7.0 / 2 |
3.5 |
% |
modulo (remainder) | 7 % 3 |
1 |
Two are worth a closer look:
Division / depends on the type.
Dividing two whole numbers gives a whole number, and everything after
the decimal point is dropped: 7 / 2 is 3. It
drops toward zero, so -7 / 2 is -3. With a
decimal in the calculation, you get a decimal: 7.0 / 2 is
3.5.
Modulo % is the remainder after whole
number division. 7 % 3 is 1 because
7 = 2 * 3 + 1. It is the standard way to ask "is this
number even?" — n % 2 is 0 for evens and
1 for positive odds. It only works on whole numbers.
fmt.Println(7 / 2) // 3
fmt.Println(7.0 / 2) // 3.5
fmt.Println(7 % 2) // 1Go has no power operator. ^ exists, but it does
something else entirely: 2 ^ 8 compiles, runs, and gives
10. To raise a number to a power, use math.Pow
from the next section.
Open exercises/13/01-arithmetic.go. Predict each printed
result before running, then run and check.
The math package
Go's math package has the helpers you reach for most.
Import it next to fmt:
import (
"fmt"
"math"
)| Function | What it does |
|---|---|
math.Floor(x) |
Largest whole value not greater than x.
Floor(3.7) is 3. |
math.Ceil(x) |
Smallest whole value not less than x.
Ceil(3.2) is 4. |
math.Abs(x) |
The absolute value. Abs(-5) is 5. |
math.Sqrt(x) |
Square root. |
math.Pow(x, y) |
x to the power y. Pow(2, 8)
is 256. |
min(a, b) |
The smaller of two numbers. Built-in, no import. |
max(a, b) |
The larger of two numbers. Built-in, no import. |
math.Pi |
The constant 3.1415... (a value, not a function). |
Call them like any other function:
fmt.Println(math.Floor(3.7)) // 3
fmt.Println(math.Ceil(3.2)) // 4
fmt.Println(math.Sqrt(16)) // 4
fmt.Println(min(7, 3)) // 3
fmt.Println(math.Pi) // 3.141592653589793These calls, and the rest of the table, are in
exercises/13/02-math-package.go.
The math functions take and return float64,
even when the result prints as 3. fmt.Println
leaves off the decimal point when a float64 has nothing after it.
min and max are built into Go and always
available; they give back the same type you gave them.
Showing decimals with
%.2f
fmt.Println prints a float64 however it sees fit. For a
fixed number of decimals, use the marker %.2f in
fmt.Printf. The 2 is the number of decimals to
show, and the value is rounded to fit:
price := 3.14159
fmt.Printf("Price: %.2f\n", price) // Price: 3.14Use it for anything a person reads.
fmt.Println(1000000.0) prints 1e+06, and
0.1 + 0.2, when held in variables, prints
0.30000000000000004, because computers store decimals only
approximately. %.2f shows these as 1000000.00
and 0.30.
Random numbers
Random numbers live in the math/rand package. Import it
next to fmt, and call it by the last part of its name,
rand:
import (
"fmt"
"math/rand"
)rand.Float64()gives a float64 from0.0up to, but not including,1.0.rand.Intn(n)gives a whole number from0ton-1. To get a six-sided die, add one:rand.Intn(6) + 1.
fmt.Println(rand.Float64()) // for example 0.6238...
fmt.Println(rand.Intn(6) + 1) // for example 4
fmt.Println(rand.Intn(100) + 1) // for example 73These lines are in exercises/13/03-random.go.
rand.Intn(6) never gives 6. Its results are
0, 1, 2, 3,
4 and 5, so the + 1 is what makes
a die. Forgetting it is a common bug, and an easy one to miss, since the
results still look random.
Mixing int and float64
Here Go is stricter than maths. Two variables of different number types cannot be combined.
Open exercises/13/05-mixed-types.go. It does not
compile:
# command-line-arguments
exercises\13\05-mixed-types.go:12:14: invalid operation: level * bonus (mismatched types int and float64)
level is an int and bonus is a float64. Fix
the line with the conversion explained below.
A conversion makes a new value of the type you name. Write the type like a function:
level := 7
bonus := 1.5
fmt.Println(float64(level) * bonus) // 10.5
total := 7
count := 2
fmt.Println(total / count) // 3
fmt.Println(float64(total) / float64(count)) // 3.5
price := 3.99
fmt.Println(int(price)) // 3Go makes you write this so that nothing changes type by accident. The
last line shows why that matters: int(price) throws away
the decimal part. It does not round, so 3.99 becomes
3. These lines are in
exercises/13/04-conversions.go.
A number written directly in the code, such as 2 or
1.5, is more flexible than a variable. Go calls it a
constant, and it takes whatever number type the
calculation needs as long as its value fits. So bonus * 2
works although bonus is a float64 and 2 looks
like an int. level * 1.5 does not work, because
1.5 cannot fit in an int:
1.5 (untyped float constant) truncated to int
Homework
Homework starters that need another package say so in their top comment. Remember that the Go extension for VS Code may add the import line for you when you save.
Problem 1 — Rectangle area
Open exercises/13/homework/01-rectangle-area.go. Two
variables hold a rectangle's width and height.
Compute the area and print it with a label, for example
Area: 24.
Problem 2 — Floor and ceil
Open exercises/13/homework/02-floor-and-ceil.go. A
variable holds the float64 3.7. Print it as it is, after
math.Floor, and after math.Ceil. Add one
comment line explaining the difference between floor and ceil.
Problem 3 — Roll two dice
Open exercises/13/homework/03-roll-two-dice.go. "Roll"
two six-sided dice and print the results like this:
Die 1: 4
Die 2: 6
Total: 10
Use rand.Intn for the rolls. Roll each die separately so
the two values are independent.
Challenge — Hypotenuse
Open exercises/13/homework/04-hypotenuse.go. Two int
variables hold the two short sides of a right triangle (a
and b). Compute and print the hypotenuse c
using the Pythagorean theorem: c is the square root of
a*a + b*b.
math.Sqrt takes a float64, so you need a conversion.
Print all three values labelled. Show the hypotenuse with two decimal
places using %.2f.
Stuck or finished? Open the homework solutions page.