Operasi modulo pada bilangan negatif dengan Python

96

Saya telah menemukan beberapa perilaku aneh dengan Python terkait bilangan negatif:

>>> -5 % 4
3

Adakah yang bisa menjelaskan apa yang sedang terjadi?

facha
sumber
25
terlihat tepat untuk saya
wheaties
6
..., -9, -5, -1, 3, 7, ...
NullUserException
3
kemungkinan duplikat C, Python - perilaku berbeda dari operasi modulo (%)
nyuszika7h
8
Anda bisa menggunakan math.fmoduntuk mendapatkan perilaku yang sama seperti di C atau Java.
0x2b3bfa0

Jawaban:

137

Tidak seperti C atau C ++, operator modulo Python ( %) selalu mengembalikan angka yang memiliki tanda yang sama dengan penyebut (pembagi). Ekspresi Anda menghasilkan 3 karena

(-5) / 4 = -1,25 -> lantai (-1,25) = -2

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

Ini dipilih daripada perilaku C karena hasil nonnegatif seringkali lebih berguna. Contohnya adalah menghitung hari kerja. Jika hari ini adalah Selasa (hari # 2), apakah hari kerja N hari sebelumnya? Dengan Python kita bisa menghitung dengan

return (2 - N) % 7

tetapi di C, jika N ≥ 3, kita mendapatkan angka negatif yang merupakan angka yang tidak valid, dan kita perlu memperbaikinya secara manual dengan menambahkan 7:

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

(Lihat http://en.wikipedia.org/wiki/Modulo_operator untuk mengetahui bagaimana tanda hasil ditentukan untuk bahasa yang berbeda.)

kennytm
sumber
6
Anehnya, operator modulo Python (%) tidak selalu mengembalikan bilangan yang memiliki tanda yang sama dengan penyebut (pembagi). Lihat stackoverflow.com/questions/48347515/…
zezollo
33

Berikut penjelasan dari Guido van Rossum:

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

Pada dasarnya, a / b = q dengan sisa r mempertahankan hubungan b * q + r = a dan 0 <= r <b.

Kevin
sumber
4
Bahasa seperti C ++ dan Java juga mempertahankan hubungan pertama, tetapi mereka membatasi hubungan negatif a, positif b, sedangkan Python floor. Itu selalu benar abs(r) < b, dan mereka berhenti r <= 0.
Evgeni Sergeev
9

Tidak ada satu cara terbaik untuk menangani pembagian integer dan mod dengan bilangan negatif. Alangkah baiknya jika a/bbesarnya sama dan tanda berlawanan (-a)/b. Alangkah baiknya jika a % bmemang sebuah modulo b. Karena kami benar-benar ingin a == (a/b)*b + a%b, dua yang pertama tidak cocok.

Yang mana yang harus disimpan adalah pertanyaan yang sulit, dan ada argumen untuk kedua belah pihak. Pembagian integer bulat C dan C ++ menuju nol (jadi a/b == -((-a)/b)), dan ternyata Python tidak.

David Thornley
sumber
1
"Alangkah baiknya jika a / b sama besarnya dan bertanda berlawanan dari (-a) / b." Mengapa itu menyenangkan? Kapan itu perilaku yang diinginkan?
pengguna76284
Karena itu kemudian akan bertindak dengan cara yang sama seperti pembagian dan perkalian reguler, dan dengan demikian secara intuitif mudah untuk dikerjakan. Itu mungkin tidak masuk akal secara matematis.
Demis
6

Seperti yang ditunjukkan, Python modulo membuat pengecualian yang beralasan untuk konvensi bahasa lain.

Ini memberikan bilangan negatif perilaku yang mulus, terutama bila digunakan dalam kombinasi dengan //operator integer-divide, seperti yang %sering dilakukan modulo (seperti dalam math. Divmod ):

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

Menghasilkan:

 -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 %selalu menghasilkan nol atau positif *
  • Python //selalu membulatkan ke arah negatif tak terhingga

* ... selama operan kanannya positif. Di samping itu11 % -10 == -9

Bob Stein
sumber
Terima kasih atas contoh Anda yang membuat saya memahaminya :)
Lamis
5

Di python , operator modulo bekerja seperti ini.

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

jadi hasilnya adalah (untuk kasus Anda):

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

sedangkan bahasa lain seperti C, JAVA, JavaScript menggunakan pemotongan, bukan floor.

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

yang mengakibatkan:

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

Jika Anda memerlukan informasi lebih lanjut tentang pembulatan dengan python, baca ini .

Munkhbold Enkhtur
sumber
3

Modulo, kelas kesetaraan untuk 4:

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

Berikut link ke perilaku modulo dengan bilangan negatif . (Ya, saya mencarinya di Google)

wheaties
sumber
@NullUserException - ya, itu benar. tetap. Terima kasih.
Wheaties
1

Saya juga berpikir itu adalah perilaku aneh Python. Ternyata saya tidak menyelesaikan pembagian dengan baik (di atas kertas); Saya memberikan nilai 0 untuk hasil bagi dan nilai -5 untuk sisanya. Mengerikan ... Saya lupa representasi geometris dari bilangan bulat. Dengan mengingat geometri bilangan bulat yang diberikan oleh garis bilangan, seseorang bisa mendapatkan nilai yang benar untuk hasil bagi dan sisanya, dan memeriksa apakah perilaku Python baik-baik saja. (Meskipun saya berasumsi bahwa Anda telah menyelesaikan masalah Anda sejak lama).

joser
sumber
1

Perlu juga disebutkan bahwa pembagian python juga berbeda dari C: Pertimbangkan

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

di C Anda mengharapkan hasilnya

0

apa x / y dengan python?

>>> print x/y
-1

dan% adalah modulo - bukan sisanya! Sedangkan x% y di C. menghasilkan

-10

python menghasilkan.

>>> print x%y
27

Anda bisa mendapatkan keduanya seperti di C

Divisi:

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

Dan sisanya (menggunakan pembagian dari atas):

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

Penghitungan ini mungkin bukan yang tercepat tetapi berfungsi untuk kombinasi tanda apa pun dari x dan y untuk mencapai hasil yang sama seperti di C plus, ini menghindari pernyataan bersyarat.

bebbo
sumber