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

Арифметика: целочисленное деление, остаток, переполнение

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

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

  • предсказывать результат / и % для целых чисел, в том числе отрицательных;
  • проверять условия сравнениями и операторами &&, ||, !;
  • читать, ставить и снимать отдельный бит в числе типа unsigned;
  • ставить скобки там, где приоритет операторов меняет смысл выражения.
Пять тарелок по три яблока под доской 17 / 5 и корзина с двумя оставшимися яблоками и биркой 17 % 5

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

C++
#include <iostream>

int main() {
    int apples = 17;
    int kids = 5;
    std::cout << apples / kids << '\n';  // 3
    std::cout << apples % kids << '\n';  // 2
}

Представьте, что семнадцать яблок раскладывают поровну на пять тарелок, по одной на каждого ребёнка. На каждую тарелку ложится по три яблока, а два остаются в корзине. Оператор / дал частное — сколько яблок на одной тарелке, а % — остаток, то есть то, что осталось в корзине.

Целочисленное деление

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

Если хотя бы один операнд имеет тип double, второй преобразуется в double, и деление сохраняет дробную часть: 17 / 5.0 равно 3.4. Целочисленное деление на 0 — неопределённое поведение: стандарт не говорит, что тогда произойдёт.

Остаток

Оператор % работает только с целыми операндами. Частное и остаток связаны: a / b * b + a % b равно a. Поэтому знак остатка совпадает со знаком делимого — левого операнда:

C++
std::cout << -17 / 5 << ' ' << -17 % 5 << '\n';  // -3 -2
std::cout << 17 / -5 << ' ' << 17 % -5 << '\n';  // -3 2

Остаток помогает разбирать число на цифры: n % 10 — последняя цифра, а n / 10 — число без неё. Для 253 это 3 и 25.

Переполнение

Тип int хранит числа из ограниченного диапазона. Там, где он занимает 4 байта, это числа от -2147483648 до 2147483647. Если результат +, - или * в этот диапазон не помещается, происходит переполнение. Для int и long long это тоже неопределённое поведение.

Беззнаковый тип unsigned (полное имя — unsigned int) хранит только неотрицательные числа, а литерал с суффиксом u, например 5u, имеет тип unsigned. Для него выход за границу определён: вычисления идут по кругу, и после максимума снова следует 0. Так работает механический счётчик пробега в машине: после последнего числа он снова показывает одни нули.

C++
#include <iostream>

int main() {
    unsigned zero = 0u;
    std::cout << zero - 1u << '\n';  // 4294967295
    std::cout << zero - 1u + 1u << '\n';  // 0
}

Число 4294967295 — максимум 4-байтового unsigned. Прибавление единицы к максимуму перешло через границу вверх, как счётчик пробега. Вычитание из нуля перешло её вниз, а этого счётчик пробега не умеет.

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

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

C++
#include <iostream>

int main() {
    double half = 5 / 2;  // ✗
    std::cout << half << '\n';  // 2
}

Компилятор ошибки не видит, и программа печатает 2. Сначала выполняется 5 / 2 — целочисленное деление, и только его результат 2 превращается в double. Тип переменной слева на деление не влияет: важны типы операндов. Напишите 5 / 2.0, и в half окажется 2.5.

Главное

  • Деление двух целых чисел отбрасывает дробную часть: 17 / 5 — это 3, а -17 / 5 — это -3.
  • % даёт остаток, и знак остатка совпадает со знаком левого операнда.
  • Переполнение int — неопределённое поведение, а unsigned идёт по кругу.
  • Дробь даёт только деление, в котором хотя бы один операнд — double.

Подпишись, чтобы пройти курс

Курс бесплатный. Войди через Telegram и подпишись на канал — откроются все уроки.

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