Arithmetic Operators in Go

A complete guide to Go's arithmetic operators (+, -, *, /, %) covering integer and floating-point division, unary operators, and common pitfalls for beginners.

Go gives you five arithmetic operators for working with numbers. Four of them work exactly the way math class prepared you for. The fifth — division on integers — behaves differently than what a calculator would show, and that single difference causes a surprising number of bugs in early Go programs.

You will use +, -, *, /, and % constantly, whether you are counting loop iterations, computing financial totals, slicing byte buffers, or laying out UI coordinates. Understanding each one at the level where you can predict its output before you run the code is what turns a tentative learner into someone who writes Go with confidence.

What Arithmetic Operators Do

An arithmetic operator takes one or two numeric values — called operands — and produces a new numeric value. Operators that take two operands, like a + b, are binary operators. Those that take one operand, like -x, are unary operators.

In Go, arithmetic operators always produce a value of the same type as their operands. If you add two int values, you get an int. If you multiply two float64 values, you get a float64. There is no implicit type promotion. If you mix types, the compiler stops and tells you to convert explicitly.

That type strictness is the single most important rule behind every surprising arithmetic result in Go.

var price int = 100
var discount float64 = 0.2
// var final = price * discount  // Compile error: mismatched types
var final = float64(price) * discount  // OK: 20.0

The explicit conversion float64(price) makes the operation possible. The compiler enforces this everywhere, which catches a whole class of accidental precision loss bugs before they ever run.

Addition, Subtraction, and Multiplication

These three operators behave intuitively regardless of numeric type. They add, subtract, or multiply the two operands and return the result.

a := 15
b := 4
sum := a + b       // 19
diff := a - b      // 11
product := a * b   // 60

The same operators work on floating-point numbers and on typed numeric constants.

width := 12.5
height := 8.0
area := width * height   // 100.0

No hidden rounding occurs here beyond the usual limits of IEEE 754 floating-point representation, which we discuss later in the chapter on numeric types.

The multiplication operator also appears frequently with durations and unit conversions.

seconds := 5
dur := time.Duration(seconds) * time.Second   // 5s

Here time.Duration is an int64 under the hood, so the multiplication is ordinary integer arithmetic wrapped in a named type.

If your output looks like this:

When you see clean, typed arithmetic with no compile errors, your type conversions are correct and Go’s type system is protecting you from unintended precision loss. That explicit conversion you had to write is a feature, not an annoyance.

Division: The Operator That Surprises Beginners

The / operator does true mathematical division when both operands are floating-point, but integer division when both operands are integers. Integer division discards the fractional part entirely — it truncates toward zero.

fmt.Println(10 / 3)   // 3, not 3.333...
fmt.Println(10.0 / 3.0) // 3.3333333333333335

If one operand is an integer and the other is a floating-point constant, the compiler still requires matching types. A common mistake is writing 10 / 3.0 and expecting a float result. Go will not compile it because 10 is an untyped integer constant and 3.0 is an untyped floating-point constant, and Go does not allow mixing them in an operation. You must be explicit.

result := float64(10) / 3.0   // 3.3333333333333335

Integer division truncates toward zero in Go. That means -7 / 3 yields -2, not -3. The sign of the result matches the sign of the mathematical quotient if you ignore the fractional part.

fmt.Println(-7 / 3)  // -2
fmt.Println(7 / -3)  // -2

This behavior is consistent with C and many other languages, but it is not universal. If you come from Python, where -7 // 3 is -3, the Go result can be surprising.

Integer division silently drops the remainder:

When you divide two integers and later use the result in further calculations — such as computing an average — the truncated value can propagate into your final output without any error message. Always check whether the computation should be done with floats before you divide.

Division by Zero

Dividing an integer by zero triggers a runtime panic. The program stops immediately.

x := 5
y := 0
// fmt.Println(x / y)  // panic: runtime error: integer divide by zero

Floating-point division by zero does not panic. It returns +Inf, -Inf, or NaN according to the IEEE 754 standard. This is a deliberate design choice in Go to match hardware behavior.

fmt.Println(1.0 / 0.0)   // +Inf
fmt.Println(-1.0 / 0.0)  // -Inf
fmt.Println(0.0 / 0.0)   // NaN

Integer division by zero kills your program:

A panic is not an error you can catch with an if statement. If there is any possibility that a denominator could be zero — from user input, a calculated value, or an empty slice length — you must guard it with a conditional check before the division.

Remainder (Modulo)

The % operator returns the remainder of integer division. It only works on integers. Applying % to floats is a compile-time error.

fmt.Println(10 % 3)  // 1
fmt.Println(10 % 2)  // 0

The sign of the remainder follows the dividend (the left operand). This is the same truncation-toward-zero rule that division uses.

fmt.Println(-7 % 3)  // -1
fmt.Println(7 % -3)  // 1

To compute a modulus that is always non-negative, you can add the divisor and apply % a second time, but that is rarely needed in idiomatic Go. Most uses of % involve cycling through indices or testing divisibility, where the sign does not vary.

// Determine if a number is even (works for positive and negative)
isEven := n%2 == 0

The remainder operator is also the tool behind alternating row colors, round-robin load balancing, and wrapping array indices.

There is no % operator for floats:

If you need the floating-point remainder, use math.Mod(x, y) from the standard library. Do not attempt to cast floats to ints to use %, because the cast truncates the value and you lose the fractional part.

Unary Plus and Minus

Go supports unary + and -. The unary plus is present in the specification mainly for symmetry with the unary minus; it has no effect on the operand.

x := 42
y := +x   // 42, identical

Unary minus negates the value.

pos := 7
neg := -pos   // -7

Unary minus also works on floating-point numbers and typed constants. There is no unary increment or decrement operator — Go treats ++ and -- as statements, not expressions, and they appear later in this chapter.

Because unary plus does nothing, you will rarely see it in real Go code. It exists so that numeric literals like +5 parse consistently across contexts.

How All Five Operators Fit Together

Most arithmetic expressions combine several operators. Go evaluates them according to operator precedence: *, /, % bind tighter than +, -. Unary operators bind tightest of all. Parentheses override precedence just as they do in math.

result := 2 + 3*4   // 14, not 20
result = (2 + 3) * 4 // 20

The full precedence table is covered in the later section on operator precedence, but the arithmetic rules are simple enough to internalize now. When in doubt, add parentheses. Code is read far more often than it is written, and parentheses make the intent immediately visible.

A missing parenthesis compiles silently:

The compiler won’t warn you that a + b * c might not mean what you think. It will faithfully compute the mathematically correct expression according to precedence. If you meant (a + b) * c but wrote a + b * c, your program compiles and runs without complaint — it just produces the wrong number. When any expression mixes operators, parentheses are cheap insurance.

Common Mistakes in Real Go Code

Some mistakes appear predictably in code reviews and forum questions. Knowing them by name will save you debugging time.

Mistake: Expecting float division from integer operands.
A developer writes average := total / count where both are int, then prints the result and wonders why it is always a whole number. The fix is explicit conversion: float64(total) / float64(count).

Mistake: Forgetting that remainder sign follows the dividend.
A negative remainder breaks a loop that expects values in the range [0, n-1]. If you rely on % for array indexing and the index might be negative, check and adjust it.

Mistake: Dividing or taking remainder by zero in production.
No compiler warning will catch this. Static analysis tools like go vet may flag obvious cases, but runtime panics from user-supplied zero values are common. Defensive checks are mandatory.

Mistake: Using % on floating-point numbers.
The compiler says invalid operation: operator % not defined on float64. This is unambiguous, but newcomers who know Python’s % operator on floats often need the reminder to use math.Mod instead.

Missing the zero guard on a denominator:

In a handler that processes incoming requests, an integer division by zero panic will crash your server unless you have a recovery middleware in place. Even with recovery, each panic wastes resources. Always guard denominators that might be zero.

Summary

Go’s arithmetic operators are a small, well-defined set: +, -, *, /, %, and the unary + and -. The biggest conceptual hurdle is integer division truncation and the type strictness that forces explicit conversions. Once you internalize that dividing two integers gives an integer result, and that the remainder operator only applies to integers, the rest follows naturally.