Operacja modulo na liczbach ujemnych w Pythonie

97

Znalazłem dziwne zachowanie w Pythonie dotyczące liczb ujemnych:

>>> -5 % 4
3

Czy ktoś mógłby wyjaśnić, co się dzieje?

facha
źródło
27
wydaje mi się dobrze
pszeniczne
6
..., -9, -5, -1, 3, 7, ...
NullUserException,
3
możliwy duplikat C, Python - inne zachowanie operacji modulo (%)
nyuszika7h
9
Możesz użyć, math.fmodaby uzyskać takie samo zachowanie, jak w C lub Javie.
0x2b3bfa0

Odpowiedzi:

142

W przeciwieństwie do C i C ++, operator modulo ( %) w Pythonie zawsze zwraca liczbę mającą ten sam znak co mianownik (dzielnik). Twoje wyrażenie daje 3, ponieważ

(-5) / 4 = -1,25 -> podłoga (-1,25) = -2

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

Jest wybierany zamiast zachowania C, ponieważ wynik nieujemny jest często bardziej przydatny. Przykładem jest obliczenie dni tygodnia. Jeśli dzisiaj jest wtorek (dzień # 2), jaki jest dzień tygodnia N dni wcześniej? W Pythonie możemy obliczać z

return (2 - N) % 7

ale w C, jeśli N ≥ 3, otrzymujemy liczbę ujemną, która jest liczbą nieprawidłową i musimy to naprawić ręcznie, dodając 7:

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

(Zobacz http://en.wikipedia.org/wiki/Modulo_operator, aby dowiedzieć się, jak znak wyniku jest określany w różnych językach).

kennytm
źródło
6
Co zaskakujące, operator modulo (%) w Pythonie nie zawsze zwraca liczbę mającą ten sam znak co mianownik (dzielnik). Zobacz stackoverflow.com/questions/48347515/…
zezollo
33

Oto wyjaśnienie Guido van Rossuma:

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

Zasadniczo jest tak, że a / b = q z resztą r zachowuje relacje b * q + r = a i 0 <= r <b.

Kevin
źródło
4
Języki takie jak C ++ i Java również zachowują pierwszą relację, ale są one górne dla negatywów a, pozytywów b, podczas gdy Python jest piętrem. To zawsze prawda abs(r) < b, a oni nie mogą r <= 0.
Evgeni Sergeev
9

Nie ma jednego najlepszego sposobu obsługi dzielenia liczb całkowitych i modów z liczbami ujemnymi. Byłoby miło, gdyby a/bbyła ta sama wielkość i przeciwny znak (-a)/b. Byłoby miło, gdyby a % brzeczywiście był modulo b. Ponieważ naprawdę tego chcemy a == (a/b)*b + a%b, pierwsze dwa są niekompatybilne.

Które zachować, to trudne pytanie i istnieją argumenty dla obu stron. C i C ++ zaokrąglają dzielenie liczb całkowitych do zera (tak a/b == -((-a)/b)), a Python najwyraźniej tego nie robi.

David Thornley
źródło
1
„Byłoby miło, gdyby a / b było tej samej wielkości i przeciwnego znaku (-a) / b”. Dlaczego miałoby to być miłe? Kiedy jest to pożądane zachowanie?
user76284
Ponieważ działałby wtedy tak samo, jak zwykłe dzielenie i mnożenie, a zatem jest intuicyjnie łatwy w użyciu. Może to jednak nie mieć sensu matematycznego.
Demis
7

Jak wskazano, Python modulo stanowi uzasadniony wyjątek od konwencji innych języków.

Daje to liczbom ujemnym płynne zachowanie, szczególnie w połączeniu z //operatorem dzielenia liczby całkowitej, jak %to często bywa w przypadku modulo (jak w matematyce. Divmod ):

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

Produkuje:

 -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 %zawsze wyświetla zero lub dodatnie *
  • Python //zawsze zaokrągla w kierunku ujemnej nieskończoności

* ... o ile właściwy operand jest dodatni. Z drugiej strony11 % -10 == -9

Bob Stein
źródło
Dzięki twojemu przykładowi zrozumiałem to :)
Lamis
6

W Pythonie operator modulo działa w ten sposób.

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

więc wynik jest (dla twojego przypadku):

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

podczas gdy inne języki, takie jak C, JAVA, JavaScript używają obcinania zamiast floor.

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

Co skutkuje w:

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

Jeśli potrzebujesz więcej informacji o zaokrąglaniu w Pythonie, przeczytaj to .

Munkhbold Enkhtur
źródło
3

Modulo, klasy równoważności dla 4:

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

Oto link do zachowania modulo z liczbami ujemnymi . (Tak, wygooglowałem to)

pszenicy
źródło
@NullUserException - tak, to było. naprawiony. Dzięki.
Wheaties
1

Pomyślałem też, że to dziwne zachowanie Pythona. Okazuje się, że nie rozwiązałem dobrze podziału (na papierze); Podawałem ilorazowi wartość 0, a pozostałej - -5. Okropne ... Zapomniałem o geometrycznej reprezentacji liczb całkowitych. Przywołując geometrię liczb całkowitych podaną przez oś liczbową, można uzyskać prawidłowe wartości ilorazu i reszty oraz sprawdzić, czy zachowanie Pythona jest w porządku. (Chociaż zakładam, że już dawno rozwiązałeś swój problem).

joser
źródło
1

Warto też wspomnieć, że również podział w pythonie jest inny niż w C: Consider

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

w C spodziewasz się wyniku

0

co to jest x / y w Pythonie?

>>> print x/y
-1

a% to modulo - nie reszta! Podczas gdy x% y w C daje

-10

Python daje.

>>> print x%y
27

Możesz uzyskać oba jak w C.

Dywizja:

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

A reszta (stosując podział z góry):

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

To obliczenie może nie jest najszybsze, ale działa dla dowolnej kombinacji znaków x i y, aby osiągnąć takie same wyniki, jak w C, a ponadto unika instrukcji warunkowych.

bebbo
źródło