Есть ли причина, по которой некоторые языки допускают отрицательный модуль?

Мне любопытно об этих языках (Java, C ...), которые игнорируют математическое определение операции модуля.

Какой смысл возвращать отрицательные значения в операции модуля (что, по определению, всегда должно возвращать положительное число)?

6 ответов

Решение

Я сомневаюсь, что оператор остатка был преднамеренно разработан, чтобы иметь ту семантику, которая, я согласен, не очень полезна. (Вы когда-нибудь написали бы программу календаря, которая показывает будние дни воскресенье, анти-суббота, анти-пятница,..., анти-понедельник для дат до эпохи?)

Скорее, отрицательные остатки являются побочным эффектом определения целочисленного деления.

A rem B := A - (A div B) * B

Если A div B определяется как trunc(A/B)Вы получаете C % оператор. Если A div B определяется как floor(A/B)Вы получаете Питона % оператор. Другие определения возможны.

Итак, настоящий вопрос:

Почему в C++, Java, C# и т. Д. Используется усеченное целочисленное деление?

Потому что так делает С.

Почему C использует усеченное деление?

Первоначально C не уточнил, как / должен обрабатывать отрицательные числа. Это оставило это до аппаратных средств.

На практике каждая существенная реализация C использовала усеченное деление, поэтому в 1999 году эта семантика была формально включена в стандарт C.

Почему в оборудовании используется усеченное деление?

Потому что это проще (= дешевле) реализовать с точки зрения беззнакового деления. Вы просто рассчитываете abs(A) div abs(B) и переверните знак, если (A < 0) xor (B < 0),

Этажное деление имеет дополнительный шаг вычитания 1 из частного, если остаток не равен нулю.

По крайней мере, в Java это не оператор модуля - это оператор остатка.

Я полагаю, что причина его выбора заключается в том, чтобы заставить эти отношения работать (из JLS):

Операция остатка для операндов, которые являются целыми числами после двоичного числового продвижения (§5.6.2), производит значение результата, такое, что (a/b)*b+(a%b) равно a. Эта идентичность сохраняется даже в частном случае, когда дивиденд является отрицательным целым числом наибольшей возможной величины для его типа, а делитель равен -1 (остаток равен 0). Из этого правила следует, что результат операции остатка может быть отрицательным, только если дивиденд является отрицательным, и может быть положительным, только если дивиденд является положительным; Более того, величина результата всегда меньше, чем величина делителя.

Это соотношение равенства кажется разумным для использования в качестве части определения. Если вы берете усечение деления в сторону нуля как данное, это оставляет вас с отрицательным остатком.

Из Википедии (мой акцент):

Для двух положительных чисел a (дивиденд) и n (делитель) модуль n (сокращенно как mod n) можно рассматривать как остаток от деления a на n. Например, выражение "5 mod 4" будет оцениваться как 1, потому что 5, деленное на 4, оставляет остаток от 1, в то время как "9 mod 3" будет оценивать до 0, потому что деление 9 на 3 оставляет остаток от 0; нечего вычитать из 9 после умножения в 3 раза 3. (Обратите внимание, что деление с помощью калькулятора не покажет вам результат, на который ссылается эта операция, частное будет выражаться в виде десятичной дроби.) Когда либо a, либо n отрицательно, это наивное определение ломается, и языки программирования отличаются в том, как эти значения определены. Хотя обычно они выполняются с целыми числами a и n, многие вычислительные системы допускают другие типы числовых операндов. Диапазон чисел для целого числа по модулю n составляет от 0 до n - 1. (n mod 1 всегда равно 0; n mod 0 не определено, что может привести к ошибке "Деление на ноль" в языках программирования). См. Модульную арифметику для более старое и родственное соглашение, применяемое в теории чисел.

Большинство из них не определены для возврата модуля. То, что они определены для возврата, является остатком, для которого положительные и отрицательные значения одинаково разумны.

В C или C++ вполне разумно сказать, что он должен производить то, что производит базовое оборудование. Это оправдание / причина не работает почти так же хорошо для Java, хотя.

Также обратите внимание, что в C89/90 и C++98/03 остаток может быть либо положительным, либо отрицательным, если результаты остатка и деления работают вместе ((a/b)*b+a%b == a). В C99 и C++11 правила были ужесточены, поэтому деление должно обрезаться до нуля, а при наличии остатка оно должно быть отрицательным.

Ни в одном из стандартов C или Java нет % упоминается как оператор модуля - скорее, он возвращает остаток.

Определено возвращать отрицательные числа для отрицательных дивидендов, чтобы соотношение (a/b)*b + a%b == a держит, пока a / b представимо. Поскольку оператор деления определен для усечения до нуля, это ограничивает знак остатка.

Прагматическая причина возврата отрицательного значения для модуля состоит в том, что аппаратная инструкция, реализующая модуль, делает это.

Таким образом, стандарты оставляют это плохо определенным, так что компиляторы могут делать с ними все, что проще.

Другие вопросы по тегам