Если оба операнда / — целые, деление тоже целое:
дробная часть не округляется, а отбрасывается в сторону нуля. Оператор %
даёт остаток от такого деления.
7 / 2 // 3, не 3.5 и не 4
7 % 2 // 1
11 / 4 // 2
11 % 4 // 3В допустимых случаях результаты связаны равенством a == (a / b) * b + a % b.
Это равенство и определяет знак остатка: раз деление усекает к нулю, остаток
обязан взять знак делимого.
Чтобы получить дробный результат, хотя бы один операнд должен быть
вещественным — иначе делить будут целые, а результат уже потом расширится
до double, когда терять нечего:
double wrong = 7 / 2; // 3.0 — деление было целым
double right = 7.0 / 2; // 3.5
double also_right = static_cast<double>(a) / b;Отрицательные числа
Начиная с C++11 деление усекает к нулю, а знак остатка совпадает со знаком делимого:
-7 / 2 // -3 (не -4: усечение к нулю, а не вниз)
-7 % 2 // -1
7 % -2 // 1
-11 / 4 // -2
-11 % 4 // -3Здесь же лежит разница со сдвигом: -7 >> 1 даёт −4, потому что сдвиг вправо
округляет вниз, а деление — к нулю. См. Побитовые операции.
Это отличается от математического определения остатка, где он неотрицателен,
и от поведения % в Python, где знак берётся от делителя. Если нужен именно
неотрицательный остаток по модулю n, его получают в два действия:
int mod = ((value % n) + n) % n; // всегда в диапазоне [0, n)Первое % приводит значение в диапазон (-n, n), прибавление n убирает
отрицательность, второе % возвращает в [0, n).
Деление на ноль
Целочисленное деление на ноль — Неопределённое поведение. Не исключение,
не бесконечность: на x86 программа обычно падает с SIGFPE, но полагаться
нельзя ни на что. Делитель, который может оказаться нулём, проверяют заранее —
и проверку ставят слева от &&, чтобы сработало короткое замыкание:
bool large = y != 0 && x / y > 2; // деления не будет при y == 0См. Логические операции и короткое замыкание.
Сообщение оболочки Floating point exception при этом не означает ни дробных
чисел, ни исключения C++ — так конкретная среда сообщает о сбое.
Есть и второй, менее известный случай UB: деление минимального знакового
значения на −1. Положительный результат не представим в том же типе, поэтому
проверки одного лишь b != 0 для произвольных знаковых данных недостаточно.
Беззнаковый тип от деления на ноль не спасает — см. Беззнаковые типы.
А у вещественных иначе: там получаются inf
и nan, и переносить это поведение на целые нельзя.
Приёмы, которые стоит знать
Деление с округлением вверх для неотрицательных целых:
int parts = (total + size - 1) / size; // вместо std::ceil(total / size)Прибавление size - 1 подталкивает результат через границу, если остаток
ненулевой. Через double и std::ceil то же самое делать не стоит: появляется
погрешность, а на больших числах — и потеря точности.
Разбор числа на разряды — % 10 даёт последнюю цифру, / 10 убирает её:
constexpr int kBase = 10;
int last_digit = value % kBase;
value /= kBase;Частые ошибки
- Ожидать 3.5 от
7 / 2и получить 3. - Считать, что
-7 % 2даст 1: даст −1. - Приводить к
doubleрезультат, а не операнд:static_cast<double>(a / b)уже поздно, деление произошло в целых. - Делить на переменную, не проверив её на ноль.