Skip to content
Theory · Operators · lesson 3 of 9

Integer sizes and overflow

In the previous lesson, Go's calculator dropped the fractional part when dividing. Now let's look at another of its rules: every integer type has a limited number of digits.

Think of the odometer in an old car. It has six wheels with digits, and it cannot show more than 999999. Drive one more kilometer, and it shows 000000: the wheels rolled over and started again.

Go's integer types work the same way. A variable of type int8 holds numbers from -128 to 127. Adding one to the maximum causes no error: the value moves to the other end of the range.

GO
package main

import "fmt"

func main() {
    var x int8 = 127
    x++
    fmt.Println(x) // -128
}

Let's walk through the program line by line:

  1. var x int8 = 127 puts into x the largest number that fits in an int8.
  2. x++ increases a variable by one, and x-- decreases it. The type stays the same, so the result stays within the int8 range.
  3. fmt.Println(x) prints -128: the number 128 does not fit in an int8, and the value moved to the other end of the range.

Here the odometer picture parts ways with Go. An odometer has no negative numbers, but an int8 does. So the maximum is followed not by zero but by the smallest negative number.

Sized types

The digits in a type's name are its number of bits, and a byte has eight bits. The more bytes, the wider the range:

TypeBytesRange
int81from -128 to 127
int162from -32768 to 32767
int324about ±2.1 billion
int648about ±9.2 · 10¹⁸
uint81from 0 to 255

Integer types fall into three groups. Let's take each one.

Signed: int8 to int64

These types hold both positive and negative numbers. About half the range goes to negatives, so an int8 has a minimum of -128 but a maximum of only 127.

You pick such a type when a value can drop below zero: a temperature, the difference of two numbers, a change in a score.

Unsigned: uint8 to uint64

Types that start with the letter u are unsigned: they hold only non-negative numbers, but their maximum is about twice as large.

  • Besides uint8 there are uint16, uint32 and uint64.
  • byte is another name for uint8.
  • Every unsigned type has a minimum of 0, so a subtraction can carry it past the lower bound.

The int type

On 64-bit systems and in the course exercises, the int type takes 8 bytes, like int64.

For ordinary integers, use int: its range is almost always enough. Sized types matter when the data format does, for example the bytes of a file are stored as uint8.

Crossing the boundary

When a result does not fit in the type's range, an overflow occurs. What follows depends on when it happens.

At run time

At run time Go does not report an overflow:

  • the minimum comes after the maximum: an int16 goes from 32767 to -32768;
  • the maximum comes below the minimum: an int8 goes from -128 to 127;
  • unsigned types behave the same way: a uint8 goes from 255 to 0, like the odometer.

Press x++ and watch the eight bits of a uint8: carries move right to left, and after 255 every bit becomes zero.

Liveuint8: eight bitsx = 253

x = 253: a uint8 has eight bits, and the largest number is 255. Press x++.

Watch the carry out of the top bit

Interviewers often ask this: what happens when an integer overflows in Go? The answer: the value silently moves to the other end of the range, with no panic and no error.

At build time

The compiler rejects a constant that does not fit in the type:

GO
var b uint8 = 256 // ✗
TEXT
cannot use 256 (untyped int constant) as uint8 value in variable declaration (overflows)

The compiler checks only the values known at build time. The statement b++ changes a variable at run time, and there is no such check there.

Common mistake

A quantity that is never negative is stored in an unsigned type, and then something is subtracted from it:

GO
package main

import "fmt"

func main() {
    var stock uint8 = 3
    stock = stock - 5  // ✗
    fmt.Println(stock) // 254
}

Neither the compiler nor the program notices an error. The difference 3 - 5 crossed zero downward and became 254, like an odometer wound backward. Store a value that can drop below zero after a subtraction in a signed type, such as int.

Key points

  • Every integer type has its own range: int8 holds -128 to 127, uint8 holds 0 to 255.
  • On overflow at run time, the value silently moves to the other end of the range.
  • The compiler rejects a constant that does not fit in the type.
  • Use int for ordinary numbers, and never keep a value that can drop below zero in an unsigned type.

Subscribe to take the course

The course is free. Sign in with Telegram and subscribe to the channel to open every lesson.

Sign in another way