Модуль работы с отрицательными значениями - странная вещь? - PullRequest
17 голосов
/ 04 сентября 2008

Подскажите, пожалуйста, сколько стоит (-2) % 5? Согласно моему интерпретатору Python 3, но есть ли у вас мудрое объяснение этому?

Я читал, что на некоторых языках результат может быть машинно-зависимым, но я не уверен, хотя.

Ответы [ 12 ]

16 голосов
/ 04 сентября 2008

Кстати: большинство языков программирования не согласились бы с Python и дали бы результат -2. В зависимости от интерпретации модуля это правильно. Однако наиболее согласованное математическое определение гласит, что модуль a и b является (строго положительным) остатком r деления a / b . Точнее, 0 <= <em>r <<em> b по определению.

13 голосов
/ 04 сентября 2008

Результат операции модуля на негативах, кажется, зависит от языка программирования, и вот список http://en.wikipedia.org/wiki/Modulo_operation

12 голосов
/ 04 сентября 2008

Ваш интерпретатор Python правильный. Один (глупый) способ вычисления модуля - вычитать или прибавлять модуль, пока полученное значение не станет между 0 и (модуль - 1).

т.д .: 13 мод 5 = (13 - 5) мод 5 = (13 - 10) мод 5 = 3

или в вашем случае: −2 mod 5 = (−2 + 5) mod 5 = 3

6 голосов
/ 12 июля 2010

Как сказано в документации Двоичные арифметические операции , Python заверяет, что:

Операторы целочисленного деления и по модулю связаны следующим тождеством: x == (x/y)*y + (x%y). Целочисленное деление и модуль также связаны со встроенной функцией divmod (): divmod(x, y) == (x/y, x%y).

И действительно,

>>> divmod(-2, 5)
(-1, 3).

Другой способ визуализировать единообразие этого метода - вычислить divmod для небольшой последовательности чисел:

>>> for number in xrange(-10, 10):
...     print divmod(number, 5)
...
(-2, 0)
(-2, 1)
(-2, 2)
(-2, 3)
(-2, 4)
(-1, 0)
(-1, 1)
(-1, 2)
(-1, 3)
(-1, 4)
(0, 0)
(0, 1)
(0, 2)
(0, 3)
(0, 4)
(1, 0)
(1, 1)
(1, 2)
(1, 3)
(1, 4)
4 голосов
/ 04 сентября 2008

Ну, 0% 5 должно быть 0, верно?

-1% 5 должно быть 4, потому что это следующая разрешенная цифра, идущая в обратном направлении (т. Е. Это не может быть 5, поскольку это вне диапазона).

И следуя этой логике, -2 должно быть 3.

Самый простой способ подумать о том, как это будет работать, состоит в том, что вы продолжаете добавлять или вычитать 5 до тех пор, пока число не упадет между 0 (включительно) и 5 ​​(исключительно).

Я не уверен насчет машинной зависимости - я никогда не видел такую ​​реализацию, но не могу сказать, что она никогда не была сделана.

3 голосов
/ 04 сентября 2008

Как объяснено в других ответах, существует много вариантов операции по модулю с отрицательными значениями. В целом, разные языки (и разные архитектуры машин) дают разные результаты.

Согласно справочному руководству Python ,

Оператор по модулю всегда дает результат с тем же знаком, что и его второй операнд (или ноль); абсолютное значение результата строго меньше, чем абсолютное значение второго операнда.

- выбор, выбранный Python. В основном по модулю определяется так, что это всегда выполняется:

x == (x/y)*y + (x%y)

, поэтому имеет смысл, что (-2)% 5 = -2 - (-2/5) * 5 = 3

0 голосов
/ 05 марта 2011

Кажется, существует общая путаница между терминами "по модулю" и "остаток".

В математике остаток * всегда должен определяться в соответствии с частным, так что если a / b == c rem d, то (c * b) + d == a. В зависимости от того, как вы округлите частное, вы получите разные остатки.

Однако по модулю всегда должен получаться результат 0 <= r < divisor, который согласуется только с делением от округления до минус бесконечности, если вы разрешите отрицательные целые числа. Если деление округляется до нуля (что является общим), то по модулю и остатку эквивалентны только для неотрицательных значений.

Некоторые языки (особенно C и C ++) не определяют требуемые поведения округления / остатка, и % является неоднозначным. Многие определяют округление до нуля, но используют термин по модулю, где остаток будет более правильным. Python относительно необычен тем, что округляется до отрицательной бесконечности, поэтому по модулю и остатку эквивалентны.

Ада округляет до нуля IIRC, но имеет операторов mod и rem.

Политика C предназначена для того, чтобы компиляторы могли выбирать наиболее эффективную реализацию для машины, но IMO - это ложная оптимизация, по крайней мере, в наши дни. Хороший компилятор, вероятно, сможет использовать эквивалентность для оптимизации везде, где отрицательное число не может появиться (и почти наверняка, если вы используете неподписанные типы). С другой стороны, там, где могут встречаться отрицательные числа, вы почти наверняка заботитесь о деталях - по причинам переносимости вы должны использовать очень тщательно разработанные сверхсложные алгоритмы и / или проверки, чтобы гарантировать, что вы получите желаемые результаты независимо от округления и остатка поведение.

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

0 голосов
/ 04 сентября 2008

Будьте осторожны, чтобы не полагаться на это поведение мода в C / C ++ на всех ОС и архитектурах. Если я правильно помню, я пытался опираться на код C / C ++, например

float x2 = x % n;

, чтобы сохранить x2 в диапазоне от 0 до n-1, но при компиляции на одной ОС появлялись отрицательные числа, но на другой ОС все было бы нормально Это привело к плохой отладке, поскольку это происходило только в половине случаев!

0 голосов
/ 04 сентября 2008

Одним из объяснений может быть то, что отрицательные числа хранятся с использованием дополнения 2 . Когда интерпретатор python пытается выполнить операцию по модулю, он преобразуется в значение без знака. Таким образом, вместо выполнения (-2)% 5 он фактически вычисляет 0xFFFF_FFFF_FFFF_FFFD% 5, что равно 3.

0 голосов
/ 04 сентября 2008

Результат зависит от языка. Python возвращает знак делителя, где, например, c # возвращает знак дивиденда (т.е. -2% 5 возвращает -2 в c #).

...