Operators and expressions

Most of Go's operators behave the way you would expect from any C-family language. There are a couple of small but important differences, especially around integer division, increment statements, and how booleans combine.

Arithmetic

The five arithmetic operators are +, -, *, /, and %. The first four work on numeric types; % is for integers only.

a, b := 10, 3

fmt.Println(a + b)   // 13
fmt.Println(a - b)   // 7
fmt.Println(a * b)   // 30
fmt.Println(a / b)   // 3   (integer division, not 3.33)
fmt.Println(a % b)   // 1   (remainder)

The one to watch is /. When both operands are integers, it performs integer division and throws away the fractional part. In the example, 10 / 3 prints 3, not 3.33. If you need a fractional result, at least one operand has to be a float. The Type conversions lesson you just finished is how you get there:

fmt.Println(float64(a) / float64(b))   // 3.3333333333333335

The remainder operator % returns what is left over after integer division. 10 % 3 is 1. It works on integers only; the math package has math.Mod for floats.

Comparison

The comparison operators are the familiar six: ==, !=, <, >, <=, >=. Each returns a bool.

fmt.Println(a > b)    // true
fmt.Println(a == b)   // false
fmt.Println(a >= 10)  // true

Comparison only works between values of the same type. Untyped literals like 1 and 1.0 adapt to a common type automatically, so 1 == 1.0 compiles and is true. But once the types are fixed, Go refuses to mix them:

var i int = 1
var f float64 = 1.0

fmt.Println(i == f)            // error: mismatched types int and float64
fmt.Println(float64(i) == f)   // fine, true

When the compiler objects, convert one side so both operands share a type.

Logical operators

The three logical operators are && (and), || (or), and ! (not). They take booleans and return a boolean.

fmt.Println(a > b && a%b == 1)   // true
fmt.Println(a < b || b > 0)      // true
fmt.Println(!(a < b))             // true

&& and || short-circuit: if the left side of && is false, Go does not evaluate the right side because the answer is already known. Same for || when the left side is true. This matters when the right side has a side effect or would panic on invalid input:

x := 0
safe := x != 0 && 10/x > 1   // 10/x is never evaluated because x != 0 is false
fmt.Println(safe)            // false, and no divide-by-zero panic

Without short-circuit, 10/x with x == 0 would crash the program. The x != 0 check guards it: because && stops as soon as the left side is false, the divide never runs. This pattern (check a condition first, then dereference or divide) is how Go handles the role that nullable-aware operators play in some other languages.

Increment and decrement are statements

x++ and x-- exist, but they are statements, not expressions. You can write x++ on its own line, but you cannot use it inside another expression:

x := 1
x++                      // fine, x is now 2
y := x++                 // compile error
fmt.Println(x++)         // compile error

Go made this change on purpose to avoid the small but persistent bugs that come from mixing an increment with another expression. If you want the new value of x elsewhere, do the increment on its own line, then use x.

Gotcha

Integer division truncates toward zero, not toward minus-infinity. 7 / 2 is 3, but -7 / 2 is -3, not -4. Try it: add fmt.Println(7 / 2) and fmt.Println(-7 / 2) and compare. The same rule applies to %: the sign of the result follows the dividend, so -7 % 2 is -1. Languages that round toward minus-infinity (Python, Ruby) would give you -4 and 1 instead, which is the single most common surprise when porting arithmetic between them and Go.

Task

Extend the starter with two more fmt.Println lines:

  • fmt.Println(a > b) should print true.
  • fmt.Println(a > b && a%b == 1) should also print true, since 10 > 3 and 10 % 3 == 1 are both true.
Expected output
13
7
30
3
1
true
true