Размеры целых и переполнение
В прошлом уроке калькулятор Go отбрасывал дробную часть при делении. Теперь посмотрим на другое его правило: у каждого целого типа ограниченное число разрядов.
Вспомните счётчик пробега в старой машине. У него шесть колёсиков с цифрами, и больше 999999 он показать не может. Проедьте ещё километр — и счётчик покажет 000000: колёсики провернулись и начали сначала.
Целые типы Go устроены так же. Переменная типа int8 хранит числа от -128 до 127. Если прибавить единицу к максимуму, ошибки не будет: значение перейдёт на другой конец диапазона.
package main
import "fmt"
func main() {
var x int8 = 127
x++
fmt.Println(x) // -128
}Давайте разберём программу по строкам:
var x int8 = 127кладёт вxсамое большое число, которое помещается вint8.x++увеличивает переменную на единицу, аx--уменьшает. Тип при этом не меняется, поэтому результат остаётся в диапазонеint8.fmt.Println(x)печатает-128: число128вint8не помещается, и значение перешло на другой конец диапазона.
Здесь аналогия со счётчиком пробега расходится с Go. У счётчика нет отрицательных чисел, а у int8 есть. Поэтому после максимума идёт не ноль, а самое маленькое отрицательное число.
Типы по размеру
Цифра в имени типа — это число бит, а бит в байте восемь. Чем больше байт, тем шире диапазон:
| Тип | Байт | Диапазон |
|---|---|---|
int8 | 1 | от -128 до 127 |
int16 | 2 | от -32768 до 32767 |
int32 | 4 | около ±2,1 миллиарда |
int64 | 8 | около ±9,2 · 10¹⁸ |
uint8 | 1 | от 0 до 255 |
Целые типы делятся на три группы. Разберём каждую.
Знаковые: от int8 до int64
Эти типы хранят и положительные, и отрицательные числа. Примерно половина диапазона отдана отрицательным, поэтому у int8 минимум -128, а максимум только 127.
Такой тип выбирают, когда значение может оказаться меньше нуля: температура, разница двух чисел, изменение счёта.
Беззнаковые: от uint8 до uint64
Типы с буквой u в начале — беззнаковые: они хранят только неотрицательные числа, зато их максимум примерно вдвое больше.
- Кроме
uint8естьuint16,uint32иuint64. byte— другое имя дляuint8.- Минимум у всех беззнаковых типов —
0, поэтому вычитание может увести их за нижнюю границу.
Тип int
На 64-битных системах и в задачах курса тип int занимает 8 байт, как int64.
Для обычных целых чисел берите int: его диапазона хватает почти всегда. Типы конкретного размера нужны, когда важен формат данных, например байты файла хранят в uint8.
Выход за границы
Если результат не помещается в диапазон типа, происходит переполнение. Что за ним последует, зависит от того, когда оно случилось.
Во время выполнения
Во время выполнения Go о переполнении не сообщает:
- после максимума идёт минимум:
int16после32767даёт-32768; - ниже минимума идёт максимум:
int8после-128даёт127; - у беззнаковых типов так же:
uint8после255даёт0, как счётчик пробега.
Нажимайте x++ и следите за восемью битами uint8: переносы бегут справа налево, а после 255 все биты становятся нулями.
x = 253: в uint8 восемь битов, самое большое число — 255. Нажмите x++.
Об этом часто спрашивают на собеседовании: что будет при переполнении целого числа в Go? Ответ: значение молча переходит на другой конец диапазона, без паники и без ошибки.
При сборке
Константу, которая не помещается в тип, компилятор не пропустит:
var b uint8 = 256 // ✗cannot use 256 (untyped int constant) as uint8 value in variable declaration (overflows)Компилятор проверяет только значения, известные при сборке. Инструкция b++ меняет переменную во время выполнения, и такой проверки там нет.
Частая ошибка
Количество, которое не бывает отрицательным, хранят в беззнаковом типе, а потом вычитают из него:
package main
import "fmt"
func main() {
var stock uint8 = 3
stock = stock - 5 // ✗
fmt.Println(stock) // 254
}Ни компилятор, ни программа ошибки не замечают. Разность 3 - 5 перешла через ноль вниз и стала 254, как счётчик, который открутили назад. Величину, которая при вычитании может уйти в минус, храните в знаковом типе, например int.
Главное
- У каждого целого типа свой диапазон: у
int8от-128до127, уuint8от0до255. - При переполнении во время выполнения значение молча переходит на другой конец диапазона.
- Константу, которая не помещается в тип, компилятор не пропускает.
- Для обычных чисел берите
int, а величину, которая может уйти в минус, не храните в беззнаковом типе.
Подпишись, чтобы пройти курс
Курс бесплатный. Войди через Telegram и подпишись на канал — откроются все уроки.