В вычислениях операция деления по модулю возвращает остаток или знаковый остаток от деления после деления одного числа на другое, называемый модулем операции.
Для двух положительных чисел a и n , модуль n (часто сокращенно a mod n ) является остатком от евклидова деления числа a на n , где a — делимое , а n — делитель . [1]
Например, выражение «5 mod 2» даст результат 1, поскольку 5 при делении на 2 даст частное 2 и остаток 1, тогда как выражение «9 mod 3» даст результат 0, поскольку 9 при делении на 3 даст частное 3 и остаток 0.
Хотя обычно выполняется с a и n , оба являющимися целыми числами , многие вычислительные системы теперь допускают другие типы числовых операндов. Диапазон значений для операции целочисленного модуля n составляет от 0 до n − 1 ( mod 1 всегда равен 0; mod 0 не определен, поскольку является делением на ноль ).
Когда хотя бы одно из чисел a или n отрицательно, базовое определение нарушается, и языки программирования различаются в том, как определяются эти значения.
В математике результатом операции по модулю является класс эквивалентности , и любой член класса может быть выбран в качестве представителя ; однако, обычным представителем является наименьший положительный остаток , наименьшее неотрицательное целое число, принадлежащее этому классу (т. е. остаток от евклидова деления ). [2] Однако возможны и другие соглашения. Компьютеры и калькуляторы имеют различные способы хранения и представления чисел; таким образом, их определение операции по модулю зависит от языка программирования или базового оборудования .
Почти во всех вычислительных системах частное q и остаток r от деления a на n удовлетворяют следующим условиям:
Это все еще оставляет неоднозначность знака, если остаток не равен нулю: возможны два варианта для остатка, один отрицательный, а другой положительный; этот выбор определяет, какой из двух последовательных частных должен использоваться для удовлетворения уравнения (1). В теории чисел всегда выбирается положительный остаток, но в вычислениях языки программирования выбирают в зависимости от языка и знаков a или n . [a] Например, стандартный Паскаль и АЛГОЛ 68 дают положительный остаток (или 0) даже для отрицательных делителей, а некоторые языки программирования, такие как C90, оставляют это на усмотрение реализации, когда либо n , либо a отрицательны (подробнее см. таблицу в разделе Языки программирования). a по модулю 0 не определено в большинстве систем, хотя некоторые определяют его как a .
Во многих реализациях используется усеченное деление , для которого частное определяется как
где - функция целой части ( округление к нулю ), т.е. усечение до нулевых значащих цифр. Таким образом, согласно уравнению ( 1 ), остаток имеет тот же знак, что и делимое a, поэтому может принимать 2| n | − 1 значений:
Дональд Кнут [3] продвигает метод деления с минимальным дроблением , для которого частное определяется как
где - функция пола ( округление вниз ). Таким образом, согласно уравнению ( 1 ), остаток имеет тот же знак, что и делитель n :
Рэймонд Т. Бут [4] продвигает евклидово деление , для которого частное определяется как
где sgn — функция знака , — функция пола ( округление вниз ), а — функция потолка ( округление вверх ). Таким образом, согласно уравнению ( 1 ), остаток неотрицателен :
Common Lisp и IEEE 754 используют округленное деление , для которого частное определяется как
где round — функция округления ( округление половины до четного ). Таким образом, согласно уравнению ( 1 ), остаток попадает между и , а его знак зависит от того, с какой стороны от нуля он попадает в эти границы:
Common Lisp также использует деление по потолку , для которого частное определяется как
где ⌈⌉ — функция потолка ( округление вверх ). Таким образом, согласно уравнению ( 1 ), остаток имеет противоположный знак делителя :
Если и делимое, и делитель положительны, то усеченное, уменьшенное и евклидово определения согласуются. Если делимое положительно, а делитель отрицателен, то усеченное и евклидово определения согласуются. Если делимое отрицательно, а делитель положительный, то усеченное и евклидово определения согласуются. Если и делимое, и делитель отрицательны, то усеченное и уменьшенное определения согласуются.
Как описывает Лейен,
Бут утверждает, что евклидово деление превосходит другие с точки зрения регулярности и полезных математических свойств, хотя floored delegate, продвигаемое Кнутом, также является хорошим определением. Несмотря на его широкое использование, усеченное деление, как показано, уступает другим определениям.
— Даан Лейен, «Деление и модуль для компьютерных ученых» [5]
Однако усеченное деление удовлетворяет тождеству . [6]
Некоторые калькуляторы имеют кнопку функции mod() , и многие языки программирования имеют похожую функцию, например, mod( a , n ) . Некоторые также поддерживают выражения, которые используют "%", "mod" или "Mod" в качестве оператора остатка или модуля , например a % n
или a mod n
.
Для сред, в которых отсутствует подобная функция, можно использовать любое из трех приведенных выше определений.
Когда результат операции по модулю имеет знак делимого (усеченное определение), это может привести к неожиданным ошибкам.
Например, чтобы проверить, является ли целое число нечетным , можно попробовать проверить, равен ли остаток от деления на 2 1:
bool is_odd ( int n ) { return n % 2 == 1 ; }
Но в языке, где modulo имеет знак делимого, это неверно, потому что когда n (делимое) отрицательно и нечетно, n mod 2 возвращает −1, а функция возвращает false.
Правильным вариантом является проверка того, что остаток не равен 0 (поскольку остаток 0 одинаков независимо от знаков):
bool is_odd ( int n ) { return n % 2 != 0 ; }
Операции по модулю могут быть реализованы таким образом, что деление с остатком вычисляется каждый раз. Для особых случаев на некоторых аппаратных средствах существуют более быстрые альтернативы. Например, модуль степеней 2 может быть альтернативно выражен как побитовая операция И (предполагая, что x — положительное целое число, или используя необрезающее определение):
x % 2n == x & (2n - 1)
Примеры:
x % 2 == x & 1
x % 4 == x & 3
x % 8 == x & 7
В устройствах и программном обеспечении, которые реализуют побитовые операции более эффективно, чем по модулю, эти альтернативные формы могут привести к более быстрым вычислениям. [7]
Оптимизации компилятора могут распознавать выражения вида expression % constant
, где constant
является степенью двойки, и автоматически реализовывать их как expression & (constant-1)
, позволяя программисту писать более понятный код без ущерба для производительности. Эта простая оптимизация невозможна для языков, в которых результат операции по модулю имеет знак делимого (включая C ), если только делимое не является целочисленным типом без знака . Это связано с тем, что если делимое отрицательно, то и модуль будет отрицательным, тогда как expression & (constant-1)
всегда будет положительным. Для этих языков вместо этого необходимо использовать эквивалентность, выраженную с помощью побитовых операций ИЛИ, НЕ и И.x % 2n == x < 0 ? x | ~(2n - 1) : x & (2n - 1)
Существуют также оптимизации для общих операций с постоянным модулем, в которых сначала вычисляется деление с использованием оптимизации с постоянным делителем .
Некоторые операции по модулю могут быть факторизованы или расширены аналогично другим математическим операциям. Это может быть полезно в криптографических доказательствах, таких как обмен ключами Диффи–Хеллмана . Свойства, включающие умножение, деление и возведение в степень, обычно требуют, чтобы a и n были целыми числами.
Кроме того, многие компьютерные системы предоставляют divmod
функционал, который производит частное и остаток одновременно. Примерами служат инструкция архитектуры x86IDIV
, функция языка программирования C div()
и функция Pythondivmod()
.
Иногда полезно, чтобы результат деления по модулю n лежал не между 0 и n − 1 , а между некоторым числом d и d + n − 1. В этом случае d называется смещением , а d = 1 встречается особенно часто.
Похоже, что для этой операции не существует стандартной нотации, поэтому давайте предварительно будем использовать a mod d n . Таким образом, у нас есть следующее определение: [60] x = a mod d n на всякий случай d ≤ x ≤ d + n − 1 и x mod n = a mod n . Очевидно, что обычная операция по модулю соответствует нулевому смещению: a mod n = a mod 0 n .
Операция по модулю со смещением связана с функцией пола следующим образом:
Чтобы увидеть это, пусть . Сначала покажем, что x mod n = a mod n . В общем случае верно, что ( a + bn ) mod n = a mod n для всех целых чисел b ; таким образом, это верно и в частном случае, когда ; но это означает, что , что мы и хотели доказать. Осталось показать, что d ≤ x ≤ d + n − 1 . Пусть k и r — целые числа, такие что a − d = kn + r с 0 ≤ r ≤ n − 1 (см. Евклидово деление ). Тогда , таким образом . Теперь возьмем 0 ≤ r ≤ n − 1 и прибавим d к обеим сторонам, получив d ≤ d + r ≤ d + n − 1 . Но мы видели, что x = d + r , так что мы закончили.
Модуль со смещением a mod d n реализован в Mathematica как Mod[a, n, d]
. [60]
Несмотря на математическую элегантность деления Кнута и евклидова деления, в языках программирования обычно гораздо чаще встречается усеченное деление по модулю. Лейен предоставляет следующие алгоритмы для вычисления двух делений, заданных усеченным целочисленным делением: [5]
/* Евклидов и Floored divmod, в стиле ldiv() языка C */ typedef struct { /* Эта структура является частью C stdlib.h, но воспроизведена здесь для ясности */ long int quot ; long int rem ; } ldiv_t ; /* Евклидово деление */ inline ldiv_t ldivE ( long numer , long denom ) { /* Языки C99 и C++11 определяют оба эти метода как усечение. */ long q = numer / denom ; long r = numer % denom ; if ( r < 0 ) { if ( denom > 0 ) { q = q - 1 ; r = r + denom ; } else { q = q + 1 ; r = r - denom ; } } return ( ldiv_t ){. quot = q , . rem = r }; } /* Деление с меньшим числом */ inline ldiv_t ldivF ( long numer , long denom ) { long q = numer / denom ; long r = numer % denom ; if (( r > 0 && denom < 0 ) || ( r < 0 && denom > 0 )) { q = q - 1 ; r = r + denom ; } return ( ldiv_t ){. quot = q , . rem = r }; }
Для обоих случаев остаток можно вычислить независимо от частного, но не наоборот. Операции здесь объединены для экономии места на экране, поскольку логические ветви одинаковы.
α|ω
вычисляет остаток при делении на .ω
α
%
должно быть усечено. [9] Стандарты до этого момента оставляли поведение, определяемое реализацией. [10]div
и mod
не подчиняются тождеству деления D = d · ( D / d ) + D % d и, таким образом, в корне нарушены.бинарный оператор % возвращает остаток от деления первого выражения на второе. .... Если оба операнда неотрицательны, то остаток неотрицателен; в противном случае знак остатка определяется реализацией.
Функция
fmod
возвращает значение
x - i * y
для некоторого целого числа
i,
такого, что если
y
не равен нулю, результат имеет тот же знак, что и
x
, и величину, меньшую величины
y
.
{{cite book}}
: CS1 maint: числовые имена: список авторов ( ссылка ){{cite book}}
: CS1 maint: числовые имена: список авторов ( ссылка )X по модулю Y, т. е. XY*INT(X/Y).
Функция остатка, т. е. XY*IP(X/Y).
Если оба операнда неотрицательны, то остаток неотрицателен. Результаты не определены, если один или оба операнда отрицательны.
Оператор % определен только в случаях, когда обе стороны положительны или обе стороны отрицательны. В отличие от C, он также работает с типами данных с плавающей точкой, а также с целыми числами.