Модульна операція з від’ємними числами в Python


94

Я виявив у Python дивну поведінку щодо від’ємних чисел:

>>> -5 % 4
3

Хтось може пояснити, що відбувається?


25
мені виглядає правильно
Wheaties

6
..., -9, -5, -1, 3, 7, ...
NullUserException


8
Ви можете використовувати math.fmodтаку ж поведінку, як у C або Java.
0x2b3bfa0

Відповіді:


136

На відміну від C або C ++, модульний оператор Python ( %) завжди повертає число, що має той самий знак, що і знаменник (дільник). Ваш вираз дає 3, тому що

(-5) / 4 = -1,25 -> підлога (-1,25) = -2

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

Його вибирають замість поведінки С, оскільки неотрицательний результат часто є більш корисним. Прикладом є обчислення днів тижня. Якщо сьогодні вівторок (день №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, щоб визначити знак результату для різних мов.)


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 за модулем робить обґрунтовано виняток із конвенцій інших мов.

Це надає негативним числам безперебійну поведінку, особливо в комбінації з //оператором цілочисельного ділення, як це %часто відбувається за модулем (як у математиці. 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


Дякую, що ваш приклад змусив мене це зрозуміти :)
Ламіс,

5

У python оператор modulo працює так.

>>> 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, прочитайте це .



1

Я також думав, що це дивна поведінка Python. Виявляється, я погано вирішував поділ (на папері); Я давав значення 0 для фактора і значення -5 для залишку. Жахливо ... Я забув геометричне зображення цілих чисел. Згадуючи геометрію цілих чисел, задану числовим рядком, можна отримати правильні значення для частки та залишку та перевірити, чи добре працює поведінка Python. (Хоча я припускаю, що ви вже давно вирішили своє занепокоєння).


1

Варто також згадати, що також поділ у python відрізняється від C: Поміркуйте

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

в С ви очікуєте результату

0

що таке x / y у python?

>>> print x/y
-1

а% за модулем - не залишок! Тоді як x% y в С дає

-10

python дає.

>>> 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, а також дозволяє уникнути умовних тверджень.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.