Перейти к содержимому
Теория · Операторы · урок 1 из 9

Арифметика и целое деление

Чему вы научитесь

Чему вы научитесь:

  • предсказывать результат целого деления и остатка, в том числе для отрицательных чисел;
  • выбирать размер целого типа и замечать переполнение, о котором Go молчит;
  • сравнивать значения и объединять условия через &&, || и !;
  • сравнивать дробные числа с допуском, а не через ==.
Взрослый делит 17 яблок между пятью детьми, каждому по три, и два яблока остаются у него в руке

Go считает, как калькулятор, но у этого калькулятора свои правила. Он помнит тип каждого числа: целое оно или дробное.

Операторы +, - и * работают с целыми числами так же, как в школьной арифметике. С делением иначе: если оба числа имеют тип int, результат тоже будет int, а у int нет дробной части.

Представьте, что вы делите яблоки между детьми. Резать яблоки нельзя, поэтому каждый получает целое число яблок, а лишние остаются у вас. Так и работает деление целых чисел:

GO
package main

import "fmt"

func main() {
    apples := 17
    kids := 5
    fmt.Println(apples / kids) // 3
    fmt.Println(apples % kids) // 2
}

Давайте посмотрим, что напечатала программа:

  1. apples / kids печатает 3: семнадцать яблок на пятерых детей — по три каждому. Оператор / дал частное.
  2. apples % kids печатает 2: два яблока остаются у вас. Оператор % дал остаток.

Обычный калькулятор показал бы 3.4, а Go показал 3. Картинка с яблоками хорошо работает для положительных чисел, а для отрицательных у Go своё правило — о нём в разделе об остатке.

Частное без дроби

Числа, с которыми работает оператор, называют операндами. Деление двух целых операндов — это целое деление: дробная часть результата отбрасывается, а не округляется.

  • 17 / 5 равно 3, хотя на бумаге получается 3.4.
  • 19 / 5 тоже равно 3, хотя 3.8 ближе к четырём.
  • -17 / 5 равно -3, а не -4: у отрицательного результата дробь тоже отбрасывается.

Получается, что целое деление всегда сдвигает результат к нулю: положительный — вниз, отрицательный — вверх.

Как получить дробь

Дробь получается при делении чисел типа float64. Число, записанное прямо в коде, называют литералом. Вот два способа получить дробный результат:

GO
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.

По третьему правилу знак остатка совпадает со знаком делимого — левого операнда:

GO
fmt.Println(-17/5, -17%5) // -3 -2
fmt.Println(17/-5, 17%-5) // -3 2

Давайте проверим обе строки:

  1. fmt.Println(-17/5, -17%5) печатает -3 -2. Частное -3, а -3 * 5 — это -15. До -17 не хватает -2, это и есть остаток.
  2. fmt.Println(17/-5, 17%-5) печатает -3 2. Делимое 17 положительное, поэтому и остаток 2 положительный, хотя делитель отрицательный.

Этот вопрос часто задают на собеседовании: какой знак у остатка, если делимое отрицательное? В математике остаток обычно считают неотрицательным, а в Go он берёт знак делимого.

Цифры числа

Остаток помогает разбирать число на цифры. Для 253:

  • n % 10 — последняя цифра, 3;
  • n / 10 — число без неё, 25.

Частая ошибка

Результат деления двух int преобразуют в float64 в надежде получить дробь:

GO
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 и подпишись на канал — откроются все уроки.

Войти другим способом