Hoạt động modulo trên số âm trong Python


94

Tôi đã tìm thấy một số hành vi kỳ lạ trong Python liên quan đến số âm:

>>> -5 % 4
3

Bất cứ ai có thể giải thích những gì đang xảy ra?


25
có vẻ phù hợp với tôi
wheaties

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


8
Bạn có thể sử dụng math.fmodđể có được hành vi tương tự như trong C hoặc Java.
0x2b3bfa0

Câu trả lời:


136

Không giống như C hoặc C ++, toán tử modulo của Python ( %) luôn trả về một số có cùng dấu với mẫu số (số chia). Biểu thức của bạn cho kết quả 3 vì

(-5) / 4 = -1,25 -> tầng (-1,25) = -2

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

Nó được chọn thay vì hành vi C vì kết quả không âm thường hữu ích hơn. Một ví dụ là tính toán các ngày trong tuần. Nếu hôm nay là thứ Ba (ngày thứ 2), thì ngày thứ N trước đó là thứ mấy? Trong Python, chúng ta có thể tính toán với

return (2 - N) % 7

nhưng trong C, nếu N ≥ 3, chúng ta nhận được một số âm là một số không hợp lệ và chúng ta cần sửa nó theo cách thủ công bằng cách thêm 7:

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

(Xem http://en.wikipedia.org/wiki/Modulo_operator để biết cách xác định dấu hiệu kết quả cho các ngôn ngữ khác nhau.)


6
Đáng ngạc nhiên là toán tử modulo của Python (%) không phải lúc nào cũng trả về một số có cùng dấu với mẫu số (số chia). Xem stackoverflow.com/questions/48347515/…
zezollo

33

Đây là lời giải thích từ Guido van Rossum:

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

Về cơ bản, sao cho a / b = q với phần dư r bảo toàn các mối quan hệ b * q + r = a và 0 <= r <b.


4
Các ngôn ngữ như C ++ và Java cũng bảo tồn mối quan hệ đầu tiên, nhưng chúng không còn là tiêu cực a, tích cực b, trong khi các tầng Python. Điều đó luôn luôn đúng abs(r) < b, và họ không ngại r <= 0.
Evgeni Sergeev

9

Không có cách nào tốt nhất để xử lý phép chia số nguyên và mod với số âm. Sẽ rất tốt nếu a/bcó cùng độ lớn và dấu hiệu ngược lại (-a)/b. Sẽ rất tốt nếua % b thực sự là một modulo b. Vì chúng tôi thực sự muốn a == (a/b)*b + a%b, hai điều đầu tiên không tương thích.

Giữ cái nào là một câu hỏi khó, và có những tranh luận cho cả hai bên. C và C ++ phân chia số nguyên tròn về 0 (như vậy a/b == -((-a)/b)), và rõ ràng Python thì không.


1
"Sẽ thật tuyệt nếu a / b có cùng độ lớn và ngược dấu với (-a) / b." Tại sao điều đó sẽ tốt? Khi đó là một hành vi mong muốn?
user76284

Bởi vì sau đó nó sẽ hoạt động giống như phép chia và phép nhân thông thường, và do đó dễ làm việc bằng trực giác. Điều đó có thể không có ý nghĩa về mặt toán học.
Demis

6

Như ra nhọn, Python modulo làm cho một nổi lập luận ngoại lệ cho các quy ước của các ngôn ngữ khác.

Điều này mang lại cho các số âm một hành vi liền mạch, đặc biệt là khi được sử dụng kết hợp với //toán tử chia số nguyên, như %modulo thường là (như trong toán học. Divmod ):

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

Sản xuất:

 -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 %luôn xuất ra số không hoặc số dương *
  • Python //luôn làm tròn về phía âm vô cực

* ... miễn là toán hạng bên phải là số dương. Mặt khác11 % -10 == -9


Cảm ơn ví dụ của bạn đã làm cho tôi hiểu nó :)
Lamis

5

Trong python , toán tử modulo hoạt động như thế này.

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

vì vậy kết quả là (đối với trường hợp của bạn):

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

trong khi các ngôn ngữ khác như C, JAVA, JavaScript sử dụng hàm cắt ngắn thay vì tầng.

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

kết quả là:

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

Nếu bạn cần thêm thông tin về làm tròn trong python, hãy đọc phần này .


3

Modulo, các lớp tương đương cho 4:

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

Đây là một liên kết đến hành vi của modulo với các số âm . (Có, tôi đã tìm kiếm nó)


@NullUserException - đúng vậy. đã sửa. Cảm ơn.
wheaties

1

Tôi cũng nghĩ đó là một hành vi kỳ lạ của Python. Nó chỉ ra rằng tôi đã không giải quyết các phép chia tốt (trên giấy); Tôi đã cho giá trị 0 cho thương số và giá trị -5 cho phần còn lại. Thật kinh khủng ... Tôi đã quên biểu diễn hình học của các số nguyên. Bằng cách nhớ lại hình học của các số nguyên được cung cấp bởi trục số, người ta có thể nhận được các giá trị chính xác cho thương số và phần dư, đồng thời kiểm tra xem hoạt động của Python có ổn không. (Mặc dù tôi cho rằng bạn đã giải quyết mối quan tâm của mình từ lâu rồi).


1

Cũng cần phải đề cập rằng cách phân chia trong python cũng khác với C:

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

trong C bạn mong đợi kết quả

0

x / y trong python là gì?

>>> print x/y
-1

và% là modulo - không phải phần còn lại! Trong khi x% y trong C cho kết quả

-10

sản lượng trăn.

>>> print x%y
27

Bạn có thể nhận được cả hai như trong C

Các bộ phận:

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

Và phần còn lại (sử dụng phép chia từ trên):

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

Tính toán này có thể không phải là nhanh nhất nhưng nó hoạt động đối với bất kỳ kết hợp dấu hiệu nào của x và y để đạt được kết quả tương tự như trong C cộng với nó tránh các câu lệnh điều kiện.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.