Операция по модулю отрицательных чисел в Python

94

Я обнаружил странное поведение в Python относительно отрицательных чисел:

>>> -5 % 4
3

Может ли кто-нибудь объяснить, что происходит?

фаха
источник
25
мне кажется
Wheaties
6
..., -9, -5, -1, 3, 7, ...
NullUserException 07
8
Вы можете использовать math.fmodдля получения того же поведения, что и в C или Java.
0x2b3bfa0

Ответы:

137

В отличие от C или C ++, оператор Python по модулю ( %) всегда возвращает число, имеющее тот же знак, что и знаменатель (делитель). Ваше выражение дает 3, потому что

(-5) / 4 = -1,25 -> этаж (-1,25) = -2

(-5)% 4 = (-2 × 4 + 3)% 4 = 3.

Его выбирают, а не поведение C, потому что неотрицательный результат часто бывает более полезным. Пример - вычисление дней недели. Если сегодня вторник (день №2), какой рабочий день за N дней? В Python мы можем выполнять вычисления с

return (2 - N) % 7

но в C, если N ≥ 3, мы получаем отрицательное число, которое является недопустимым числом, и нам нужно вручную исправить его, добавив 7:

int result = (2 - N) % 7;
return result < 0 ? result + 7 : result;

(См. Http://en.wikipedia.org/wiki/Modulo_operator, чтобы узнать, как определяется знак результата для разных языков.)

Kennytm
источник
6
Удивительно, но оператор модуля Python (%) не всегда возвращает число, имеющее тот же знак, что и знаменатель (делитель). См stackoverflow.com/questions/48347515/...
zezollo
33

Вот объяснение от Гвидо ван Россума:

http://python-history.blogspot.com/2010/08/why-pythons-integer-division-floors.html

По сути, это так, что a / b = q с остатком r сохраняет отношения b * q + r = a и 0 <= r <b.

Кевин
источник
4
Такие языки, как C ++ и Java, также сохраняют первую взаимосвязь, но допускают отрицательное aи положительное b, тогда как Python - нижние. Это всегда правда abs(r) < b, и если и только если r <= 0.
Евгений Сергеев
9

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

Какой из них оставить - вопрос сложный, и есть аргументы для обеих сторон. C и C ++ округляют целочисленное деление до нуля (так a/b == -((-a)/b)), а Python, очевидно, этого не делает.

Дэвид Торнли
источник
1
«Было бы хорошо, если бы a / b было той же величины и противоположным знаком (-a) / b». Почему это было бы хорошо? Когда это желаемое поведение?
user76284
Потому что тогда он будет действовать так же, как обычное деление и умножение, и с ним интуитивно легко работать. Хотя математически это может не иметь смысла.
Демис
6

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

Это обеспечивает плавное поведение отрицательных чисел, особенно при использовании в сочетании с //оператором целочисленного деления, как это %часто бывает по модулю (как в math. Divmod ):

for n in range(-8,8):
    print n, n//4, n%4

Производит:

 -8 -2 0
 -7 -2 1
 -6 -2 2
 -5 -2 3

 -4 -1 0
 -3 -1 1
 -2 -1 2
 -1 -1 3

  0  0 0
  1  0 1
  2  0 2
  3  0 3

  4  1 0
  5  1 1
  6  1 2
  7  1 3
  • Python %всегда выводит ноль или положительный результат *
  • Python //всегда округляется до отрицательной бесконечности

* ... пока правильный операнд положительный. С другой стороны11 % -10 == -9

Боб Штайн
источник
Спасибо, ваш пример помог мне понять это :)
Lamis
5

В Python оператор по модулю работает следующим образом.

>>> mod = n - math.floor(n/base) * base

так что результат (для вашего случая):

mod = -5 - floor(-1.25) * 4
mod = -5 - (-2*4)
mod = 3

в то время как другие языки, такие как C, JAVA, JavaScript, используют усечение вместо пола.

>>> mod = n - int(n/base) * base

что приводит к:

mod = -5 - int(-1.25) * 4
mod = -5 - (-1*4)
mod = -1

Если вам нужна дополнительная информация об округлении в python, прочтите это .

Мунхболд Энхтур
источник
3

По модулю, классы эквивалентности для 4:

  • 0: 0, 4, 8, 12 ... и -4, -8, -12 ...
  • 1: 1, 5, 9, 13 ... и -3, -7, -11 ...
  • 2: 2, 6, 10 ... и -2, -6, -10 ...
  • 3: 3, 7, 11 ... и -1, -5, -9 ...

Вот ссылка на поведение по модулю с отрицательными числами . (Да, я погуглил)

пшеница
источник
@NullUserException - да, было. фиксированный. Спасибо.
Wheaties 07
1

Я тоже подумал, что это странное поведение Python. Оказывается, я плохо решал деление (на бумаге); Я давал значение 0 частному и значение -5 для остатка. Ужасно ... Я забыл геометрическое представление целых чисел. Вспомнив геометрию целых чисел, заданных числовой линией, можно получить правильные значения частного и остатка и проверить правильность поведения Python. (Хотя предполагаю, что вы уже давно решили свою проблему).

Joser
источник
1

Также стоит упомянуть, что разделение в python отличается от C: рассмотрим

>>> x = -10
>>> y = 37

в C вы ожидаете результата

0

что такое x / y в питоне?

>>> print x/y
-1

а% - по модулю, а не остаток! В то время как x% y в C дает

-10

питон дает.

>>> print x%y
27

Вы можете получить и то, и другое, как в C

Дивизион:

>>> from math import trunc
>>> d = trunc(float(x)/y)
>>> print d
0

И остальное (используя деление сверху):

>>> r = x - d*y
>>> print r
-10

Этот расчет, возможно, не самый быстрый, но он работает для любых сочетаний знаков x и y для достижения тех же результатов, что и в C, плюс избегает условных операторов.

беббо
источник