Остаток от деления и деление по модулю

Взятие остатка от деления и деление по модулю — разные операции. В некоторых языках программирования оператор % — это взятие остатка, в некоторых он же выполняет деление по модулю. А в некоторых для каждой операции предусмотрены соответствующие функции (например, remainderBy и modBy в Elm или rem и mod в Clojure).

Вспомним формулу для нахождения остатка rr от деления aa на bb:

a=bq+ra = b \cdot q + r

В математике остаток от деления по определению всегда положительный: rr должен быть больше нуля и меньше абсолютного значения делителя. В программировании, если знаки делимого и делителя не совпадают, может быть по-разному.

Python, например, всегда сохраняет знак делителя:

# Python
10 % -3 # -2
-10 % 3 # 2

JS, наоборот, сохраняет знак делимого делимого.

// JavaScript
10 % -3 // 1
-10 % 3 // -1

— Знаки ладно, но почему значения получаются разные?

Потому что Python выполняет деление по модулю, а JS — взятие остатка. В документации это также отражено: у Питона операция называется "modulo", у JS — "remainder". Если делимое и делитель оба положительные или оба отрицательные, то результат будет одинаковым. Но если знаки разные, то возникают нюансы.

Например, при выполнении r = -10%3 нужно вычислить q = -10//3 (целочисленное деление). Но в какую сторону округлять отрицательный результат -3.333...? Здесь мнения расходятся. Кто-то считает, что должно быть -4 и округляет в сторону минусов (к полу, floor); кто-то округляет в сторону нуля (к потолку, ceil) и получает -3.

В Python используется floor и будет -4:

# Python
-10 // 3 # -4

В Clojure/C/Elm используется ceil и будет -3:

; Clojure
(quot -10 3) ; -3

При делении по модулю эта ситуация имеет значение. Модульная арифметика оперирует множествами, в которых важно «направление» вычислений (отрицательное значение — это как развернуть множество задом-наперёд, см. пример с часами). При взятии остатка нам просто надо найти величину, которой не хватает до кратного «влезания» делителя в делимое.

В подавляющем большинстве случаев нам нужен остаток от деления двух целых чисел, поэтому создатели многих языков решили ограничится одним оператором %. Но, например, в Python он находится через деление по модулю, а в JS — через взятие остатка. А где-то поступили проще и явно предусмотрели две функции: modulo и remainder.

Материалы