Содержание урока · 10 разделов
- Целые числа: int и его родственники
- Основные целочисленные типы
- Дробные числа: float64 как выбор по умолчанию
- Целочисленное деление: почему 7 / 2 — это 3
- Преобразование типов: только руками
- Переполнение: что происходит на границе
- byte и rune: числа в маске
- Песочница урока
- Коротко: что запомнить
- Вопросы и ответы
Целые числа: int и его родственники#
Основной целочисленный тип в Go — int. Когда вы пишете x := 42, компилятор создаёт именно int, если правая часть — целое число без явного типа. На большинстве платформ это 64-битное знаковое целое с диапазоном примерно от -9,2 млрд миллионов до +9,2 млрд миллионов — для счётчиков, длин массивов и повседневной арифметики хватает с запасом.
Но int — не единственный вариант. Есть семейство фиксированных размеров: int8 (от -128 до 127), int16, int32, int64 — и их беззнаковые братья uint, uint8, uint16, uint32, uint64, которые принимают только неотрицательные значения. Зачем они нужны? Когда размер важен: uint8 (у него есть псевдоним byte) лежит в основе работы с байтами, int64 встречается в метках времени и базах данных, int32 — в протоколах обмена.
Практическое правило: по умолчанию пишите int и не думайте о размере. Явные типы берите тогда, когда формат данных диктует их — сетевой протокол, бинарный файл, стыковка с базой. Для новичка выбор «int для всего целого» — не лень, а идиома.
Основные целочисленные типы#
| Тип | Размер | Диапазон | Когда использовать |
|---|---|---|---|
| int | зависит от платформы (обычно 64 бита) | -9,2 квинтиллиона до 9,2 квинтиллиона | по умолчанию для всех целых |
| int8 | 8 бит | -128 до 127 | компактные знаковые значения |
| int32 | 32 бита | около -2,1 млрд до 2,1 млрд | протоколы, руны (rune = int32) |
| int64 | 64 бита | около ±9,2 квинтиллиона | метки времени, большие счётчики |
| uint8 (byte) | 8 бит | 0 до 255 | байты, байтовые срезы |
| uint64 | 64 бита | 0 до 18,4 квинтиллиона | хэши, битовые маски без знака |
Дробные числа: float64 как выбор по умолчанию#
Дробные числа в Go живут в двух типах: float64 и float32. Если тип не указан, pi := 3.14 создаёт float64 — и это правильный выбор по умолчанию. float64 хранит около 15-16 значащих цифр и покрывает почти все бытовые задачи: физика, координаты, статистика. float32 экономит память (4 байта вместо 8), но даёт лишь около 7 цифр точности — его берут для больших массивов данных, графики и машинного обучения, где объём важнее точности.
Важно понимать: float — это двоичное представление дроби, а не десятичное. Числа вроде 0.1 точно не представляются, из-за чего 0.1 + 0.2 оказывается 0.30000000000000004. Это не баг Go, а свойство IEEE 754 — так работают float в любом современном языке. Именно поэтому дробные деньги в финансах хранят в целых копейках, а не во float.
Пара синтаксических мелочей: запись 3. (три и точка) легальна и равна 3.0, а экспонента пишется через e: 1e9, 2.5e-3.
Целочисленное деление: почему 7 / 2 — это 3#
Самая частая ловушка новичка в Go — деление целых чисел. Оператор / для двух целых типов выполняет целочисленное деление: дробная часть отбрасывается без округления. 7 / 2 — это 3, 1 / 2 — это 0, -7 / 2 — это -3 (усечение к нулю, а не в меньшую сторону).
Хотите честное деление? Хотя бы один операнд должен быть float: 7.0 / 2 даст 3.5. Обычно это делают явным преобразованием: float64(x) / 2.
Рядом с / живёт оператор остатка %: 7 % 2 — это 1. Он незаменим для проверки чётности (n % 2 == 0), циклических индексов (i % len(list)) и разбиения на страницы. Запомните связку: деление целых усекает, остаток возвращает то, что «не поделилось».
Коварный момент: ошибка проявляется не в месте деления, а далеко потом, когда результат неожиданно оказался целым. Если переменная должна быть дробной, проверяйте типы всех участников выражения.
Песочница: деление и остаток
Запустите код и сверьте вывод с ожиданиями. Затем замените apple := 10 на apple := 10.0 — программа не скомпилируется, потому что % не работает с float. Верните 10 и поменяйте friends на 4: сколько достанется каждому?
Запустите код, чтобы увидеть вывод.
Проверьте себя: целочисленное деление
Что напечатает `fmt.Println(9 / 2)`?
Преобразование типов: только руками#
Go не выполняет неявных преобразований между числовыми типами. Сложить int и float64 напрямую нельзя — программа не скомпилируется с ошибкой «mismatched types int and float64». Преобразование пишется явно, как вызов функции: float64(x), int(pi), byte(n).
Это может раздражать после JavaScript, где всё складывается со всем. Но у строгого подхода есть цена и выгода: цена — чуть больше печатания; выгода — в коде никогда не происходит «магических» потерь данных. Посмотрев на строку total := float64(count) * price, вы точно знаете, где случилось преобразование.
При преобразовании вниз данные теряются по правилам усечения. int(99.99) — это 99: дробная часть отбрасывается, никакого округления. Для честного округления используется math.Round из одноимённого пакета. А byte(300) даст 44: 300 не помещается в диапазон 0-255, и лишние старшие биты отсекаются (300 - 256 = 44).
Явные преобразования в действии
GOpackage main
import "fmt"
func main() {
price := 99.99
whole := int(price) // усечение, не округление
fmt.Println("Целая часть:", whole)
count := 3
total := float64(count) * price
fmt.Println("Сумма:", total)
fmt.Println("byte(300):", byte(300))
}int(price) отбрасывает 0.99, не округляя. float64(count) превращает 3 в 3.0, после чего умножение проходит без ошибок типов. byte(300) демонстрирует отсечение битов: 300 выходит за диапазон byte, остаётся 300 - 256 = 44.
Что выведет код?
Прочитайте код и выберите, что он выведет:
package main
import "fmt"
func main() {
x := 7
y := 2
fmt.Println(x/y, float64(x)/y)
}Переполнение: что происходит на границе#
Каждый целочисленный тип ограничен. У int8 диапазон от -128 до 127 — и добавление единицы к 127 в настоящем Go не вызывает ни ошибки, ни исключения: значение «заворачивается» и становится -128. Такой инцидент называется переполнением, и компилятор его не ловит — переменная тихо содержит бессмысленное число.
Переполнение бьёт по конкретным местам: вычитание в счётчиках обратного отсчёта, умножение больших чисел, суммы по длинным массивам. Классика — неаккуратный uint: он не может быть отрицательным, поэтому u - 1 при u = 0 даёт максимальное значение типа, а не -1. История программирования полна уязвимостей, построенных на этом эффекте.
Как защищаться? Первое: работайте с int — у него диапазон настолько велик, что переполнение в учебных задачах недостижимо. Второе: если данные приходят извне и теоретически велики, проверяйте границы перед арифметикой. Третье: для по-настоящему больших чисел в стандартной библиотеке есть math/big — произвольная точность ценой скорости.
В Go переполнение целых — это тихое заворачивание значения, а не ошибка времени выполнения. Программа продолжит работать с неверным числом. Именно поэтому места с потенциально большими вычислениями стоит проверять на границы вручную — или использовать типы большего размера.
byte и rune: числа в маске#
Два типа из этого урока заслуживают отдельного знакомства, потому что они притворяются не числами. byte — это псевдоним uint8, то есть число от 0 до 255. rune — псевдоним int32, число-код символа в таблице Unicode. Технически это целые числа; семантически они описывают байт и символ.
Псевдоним здесь означает дословно: byte и uint8 — один и тот же тип, можно даже присваивать друг другу без преобразований. То же верно для rune и int32.
Зачем знать это уже сейчас? Во-первых, чтобы не удивляться, когда строковый индекс возвращает число: s[0] у строки — это byte, то есть 65 для «A». Во-вторых, чтобы понимать сообщения об ошибках вроде «cannot use rune (variable of type rune) as int32». А подробный разговор о строках, байтах и рунах — тема следующего урока, где мы будем резать строки и считать кириллические символы.
Проверьте себя: результат int()
Что напечатает `fmt.Println(int(2.7), int(-2.7))`?
Соберите код: преобразование типов
Впишите типы преобразований так, чтобы программа считала средний балл и напечатала его без дробной части.
Карточки: числовые типы
Песочница урока
Редактируйте код и запускайте его прямо здесь — интерпретатор работает в браузере, ничего устанавливать не нужно. За каждый запуск — +2 XP.
Запустите код, чтобы увидеть вывод.
Коротко: что запомнить
- По умолчанию целые — это int, дробные — float64; явные типы нужны для форматов данных.
- Деление двух целых усекает дробную часть: 7/2 = 3; остаток берётся через %.
- Хотя бы один float-операнд — и деление станет вещественным: 7.0/2 = 3.5.
- Преобразования типов только явные: int(99.99) = 99, float64(3) = 3.0.
- byte = uint8, rune = int32 — числовые типы с символьной семантикой.
- Переполнение заворачивает значение молча: 127 + 1 для int8 даёт -128.
- 0.1 + 0.2 не равно 0.3 — это свойство двоичных дробей, а не баг Go.
Домашнее задание
Напишите программу с двумя константами: totalSeconds = 3661 и price = 79.5. Вычислите и выведите: полные часы (целочисленное деление), остаток секунд (остаток), а также стоимость трёх с половиной единиц товара, приведя типы корректно (подсказка: 3.5 умножить на float64 от price не сработает — price уже float64). Проверьте вывод на глазах: часы = 1, остаток = 61, стоимость = 278.25.
Личные заметкиПусто
Вопросы и ответы по уроку
Какой тип выбрать: int или int64?
По умолчанию int — на современных 64-битных платформах он совпадает с int64. Явный int64 нужен там, где размер зафиксирован контрактом: сериализация, база данных, сетевой протокол. Для счётчиков, длин и индексов всегда хватает int.
Почему 0.1 + 0.2 != 0.3?
Дробные числа хранятся в двоичном виде по стандарту IEEE 754, а 0.1 в двоичной системе — бесконечная дробь. Небольшая погрешность накапливается, и сумма получается 0.30000000000000004. Для денег используйте целые копейки или пакеты вроде shopspring/decimal.
Как округлить float до целого?
int(x) не округляет, а усекает. Для округления используйте math.Round(x) — он вернёт float64, который можно привести к int: int(math.Round(2.7)) даст 3. Есть также math.Floor и math.Ceil.
Термины урока
Ключевые понятия из этого урока — короткие определения в глоссарии. Наведите на термин, чтобы вспомнить, откройте карточку для подробностей и ссылки на практику.
Изучайте дальше
Следующая ступень учебного пути
Продвинутые возможности Go: от замыканий до рефлексии
Продвинутый курс Go: замыкания, defer, горутины, каналы, контекст, интерфейсы, дженерики, рефлексия, тесты и JSON. 25 уроков с практикой в браузере.
- Писать и читать конкурентный код: go-функции, каналы, select
- Управлять жизненным циклом операций через context
- Проектировать API на интерфейсах
- Использовать дженерики без злоупотребления