Numbers and Math
Go does arithmetic with the usual operators, but integer division, remainders of negative numbers and division by zero have rules worth knowing. This lesson covers those, the built-in min and max, the math package, random numbers with math/rand/v2, and arbitrary-size numbers with math/big.
Arithmetic
package main import "fmt" func main() { a, b := 17, 5 fmt.Println(a+b, a-b, a*b) fmt.Println(a/b, a%b) // integer division and remainder x, y := 17.0, 5.0 fmt.Println(x / y) }
22 12 85 3 2 3.4
When both operands are integers, / gives an integer result and discards the fraction. To get 3.4 from two int variables you must convert them first: float64(a) / float64(b) (lesson 9).
Negative numbers
Integer division truncates toward zero, and the remainder takes the sign of the left operand:
package main import "fmt" func main() { fmt.Println(-7/2, -7%2) fmt.Println(7/-2, 7%-2) // a remainder that is never negative, e.g. for wrapping around a clock hour := -3 fmt.Println(((hour % 24) + 24) % 24) }
-3 -1 -3 1 21
Division by zero
Dividing an integer by a constant zero is a compile error. Dividing by a variable that turns out to be zero crashes the program at run time with a panic (lesson 46). Floating-point division by zero doesn't crash — it produces infinity or "not a number":
package main import "fmt" func main() { zero := 0.0 fmt.Println(1/zero, -1/zero, zero/zero) items, boxes := 10, 0 fmt.Println(items / boxes) }
+Inf -Inf NaN panic: runtime error: integer divide by zero goroutine 1 [running]: … exit status 2
Check divisors that come from user input or data before you divide.
Increment and compound assignment
package main import "fmt" func main() { score := 10 score++ score += 5 score *= 2 score -= 1 score /= 3 score %= 7 fmt.Println(score) }
3
++ and -- are statements in Go, not expressions: you can write score++ on its own line, but not x := score++ or ++score.
min and max
The built-in functions min and max take any number of arguments of the same ordered type — integers, floats or even strings:
package main import "fmt" func main() { fmt.Println(min(4, 9, 2), max(4, 9, 2)) fmt.Println(max(0.5, 1.25)) fmt.Println(min("pear", "apple", "fig")) volume := 130 volume = min(max(volume, 0), 100) // clamp to 0..100 fmt.Println(volume) }
2 9 1.25 apple 100
The math package
package main import ( "fmt" "math" ) func main() { fmt.Println(math.Sqrt(2), math.Pow(2, 10), math.Cbrt(27)) fmt.Println(math.Abs(-4.2), math.Hypot(3, 4)) fmt.Println(math.Floor(2.7), math.Ceil(2.1), math.Trunc(-2.7)) fmt.Println(math.Round(2.5), math.Round(-2.5), math.RoundToEven(2.5)) fmt.Println(math.Mod(7.5, 2), math.Log(math.E), math.Log10(1000)) fmt.Printf("%.4f %.4f\n", math.Pi, math.Sin(math.Pi/2)) fmt.Println(math.Inf(1), math.IsNaN(math.NaN())) }
1.4142135623730951 1024 3 4.2 5 2 3 -2 3 -3 2 1.5 1 3 3.1416 1.0000 +Inf true
The math functions work with float64. math.Round rounds halves away from zero; math.RoundToEven uses "banker's rounding". To round to two decimal places for display, format with %.2f rather than rounding the number itself.
Random numbers
Use math/rand/v2 for games, simulations and shuffling. Its top-level functions are automatically seeded, so each run differs. For reproducible results — tests, or this course's examples — create your own generator from a fixed seed:
package main import ( "fmt" "math/rand/v2" ) func main() { r := rand.New(rand.NewPCG(1, 2)) // fixed seed: same numbers every run fmt.Println(r.IntN(6)+1, r.IntN(6)+1, r.IntN(6)+1) // dice rolls, 1..6 fmt.Printf("%.3f\n", r.Float64()) // in [0, 1) cards := []string{"A", "K", "Q", "J"} r.Shuffle(len(cards), func(i, j int) { cards[i], cards[j] = cards[j], cards[i] }) fmt.Println(cards) fmt.Println(rand.N(100) < 100) // top-level: random seed each run }
5 4 5 0.430 [Q K J A] true
For passwords, tokens and keys, use crypto/rand instead — math/rand/v2 is fast but predictable, so it's not safe for security.
Arbitrarily large numbers
When even int64 isn't big enough, math/big provides integers (and rationals and floats) of any size:
package main import ( "fmt" "math/big" ) func main() { f := big.NewInt(1) for i := int64(2); i <= 30; i++ { f.Mul(f, big.NewInt(i)) } fmt.Println("30! =", f) }
30! = 265252859812191058636308480000000
big values are changed by methods (f.Mul(f, x) means "set f to f × x") rather than by operators, and they're much slower than built-in types, so use them only when you need them.
Try it yourself
Split a bill: work out each person's share in cents and the leftover cents that can't be split evenly.
each pays 33.33, 1 cent(s) left over