Арифметика и целое деление
Чему вы научитесь:
- предсказывать результат целого деления и остатка, в том числе для отрицательных чисел;
- выбирать размер целого типа и замечать переполнение, о котором Go молчит;
- сравнивать значения и объединять условия через
&&,||и!; - сравнивать дробные числа с допуском, а не через
==.

Go считает, как калькулятор, но у этого калькулятора свои правила. Он помнит тип каждого числа: целое оно или дробное.
Операторы +, - и * работают с целыми числами так же, как в школьной арифметике. С делением иначе: если оба числа имеют тип int, результат тоже будет int, а у int нет дробной части.
Представьте, что вы делите яблоки между детьми. Резать яблоки нельзя, поэтому каждый получает целое число яблок, а лишние остаются у вас. Так и работает деление целых чисел:
package main
import "fmt"
func main() {
apples := 17
kids := 5
fmt.Println(apples / kids) // 3
fmt.Println(apples % kids) // 2
}Давайте посмотрим, что напечатала программа:
apples / kidsпечатает3: семнадцать яблок на пятерых детей — по три каждому. Оператор/дал частное.apples % kidsпечатает2: два яблока остаются у вас. Оператор%дал остаток.
Обычный калькулятор показал бы 3.4, а Go показал 3. Картинка с яблоками хорошо работает для положительных чисел, а для отрицательных у Go своё правило — о нём в разделе об остатке.
Частное без дроби
Числа, с которыми работает оператор, называют операндами. Деление двух целых операндов — это целое деление: дробная часть результата отбрасывается, а не округляется.
17 / 5равно3, хотя на бумаге получается3.4.19 / 5тоже равно3, хотя3.8ближе к четырём.-17 / 5равно-3, а не-4: у отрицательного результата дробь тоже отбрасывается.
Получается, что целое деление всегда сдвигает результат к нулю: положительный — вниз, отрицательный — вверх.
Как получить дробь
Дробь получается при делении чисел типа float64. Число, записанное прямо в коде, называют литералом. Вот два способа получить дробный результат:
a, b := 17, 5
fmt.Println(7.0 / 2) // 3.5
fmt.Println(float64(a) / float64(b)) // 3.4Дробный литерал: 7.0 / 2
Литерал с точкой, например 7.0, уже дробный. Литерал 2 рядом с ним подстраивается под дробный тип, и получается 3.5.
Этот способ подходит, когда числа записаны прямо в коде. С переменными он не работает: переменная уже имеет свой тип и подстраиваться не умеет.
Преобразование переменных: float64(a) / float64(b)
Запись float64(a) берёт значение целой переменной a и превращает его в дробное. Сама a при этом остаётся int.
- Преобразуйте обе переменные: тогда оба операнда —
float64, и результат —3.4. - Если преобразовать только одну,
float64(a) / bне скомпилируется: типы операндов разные.
Деление на ноль
Что произойдёт при делении целого числа на ноль, зависит от того, когда ноль становится известен.
Ноль известен при сборке
Деление на литерал 0 не компилируется: invalid operation: division by zero. Компилятор выдаёт то же сообщение при делении на константу, равную нулю, например на z после const z = 0.
Компилятор видит это значение заранее и находит ошибку ещё до запуска.
Ноль в переменной
Если нулём оказалась целая переменная, компилятор об этом не знает. Программа запускается и выполняет строки до деления. На самом делении она аварийно завершается с сообщением panic: runtime error: integer divide by zero.
Такую аварийную остановку программы называют паникой. Если переменная может оказаться нулём, проверяйте её перед делением.
Остаток
Оператор % подчиняется трём правилам:
- работает только с целыми операндами;
- выполняется наравне с
*и/, слева направо; - связан с частным:
a / b * b + a % bравноa.
По третьему правилу знак остатка совпадает со знаком делимого — левого операнда:
fmt.Println(-17/5, -17%5) // -3 -2
fmt.Println(17/-5, 17%-5) // -3 2Давайте проверим обе строки:
fmt.Println(-17/5, -17%5)печатает-3 -2. Частное-3, а-3 * 5— это-15. До-17не хватает-2, это и есть остаток.fmt.Println(17/-5, 17%-5)печатает-3 2. Делимое17положительное, поэтому и остаток2положительный, хотя делитель отрицательный.
Этот вопрос часто задают на собеседовании: какой знак у остатка, если делимое отрицательное? В математике остаток обычно считают неотрицательным, а в Go он берёт знак делимого.
Цифры числа
Остаток помогает разбирать число на цифры. Для 253:
n % 10— последняя цифра,3;n / 10— число без неё,25.
Частая ошибка
Результат деления двух int преобразуют в float64 в надежде получить дробь:
package main
import "fmt"
func main() {
total := 5
count := 2
half := float64(total / count) // ✗
fmt.Println(half) // 2
}Компилятор ошибки не видит, и программа печатает 2. Сначала Go выполняет total / count как целое деление, и только его результат 2 становится float64. Преобразуйте операнды, а не результат: float64(total) / float64(count) даёт 2.5.
Главное
- Деление двух целых отбрасывает дробную часть:
17 / 5— это3, а-17 / 5— это-3. - Оператор
%даёт остаток, и его знак совпадает со знаком делимого. - Чтобы получить дробь, преобразуйте в
float64операнды, а не результат. - Деление на литерал
0не компилируется, а на целую переменную, равную нулю, вызывает панику.
Подпишись, чтобы пройти курс
Курс бесплатный. Войди через Telegram и подпишись на канал — откроются все уроки.