2009-12-15 26 views
58

Tôi đã tìm thấy rằng cùng một hoạt động mod tạo ra các kết quả khác nhau tùy thuộc vào ngôn ngữ đang được sử dụng.C, Python - hành vi khác nhau của hoạt động modulo (%)

Trong Python:

-1 % 10 

sản xuất

Trong C nó tạo -1!

1) Cái nào là modulo đúng?
2) Làm thế nào để hoạt động mod trong C giống như trong Python?

Trả lời

60
  1. Cả hai phiên bản là chính xác, tuy nhiên trong toán học (lý thuyết số đặc biệt), modulo của Python được sử dụng phổ biến nhất.
  2. Trong C, bạn thực hiện ((n % M) + M) % M để nhận kết quả tương tự như trong Python. Ví dụ. ((-1 % 10) + 10) % 10. Lưu ý, làm thế nào nó vẫn hoạt động cho các số nguyên dương: ((17 % 10) + 10) % 10 == 17 % 10, cũng như cho cả hai biến thể của C triển khai (phần còn lại tích cực hoặc tiêu cực).
+3

Điều gì aout n = - 11? Tôi nghĩ rằng bạn có nghĩa là ((n% M) + M)% M – Henrik

+0

với (-17 + 10)% 10 bạn đang ở trong cùng một vấn đề. – yeyeyerman

+0

Rất tiếc, đã sửa. Cảm ơn. –

5

Cả hai câu trả lời đều chính xác vì -1 modulo 10 giống với 9 modulo 10.

r = (a mod m) 
a = n*q + r 

Bạn có thể chắc chắn rằng |r| < |n|, nhưng không phải những gì giá trị của r là. Có 2 câu trả lời, tiêu cực và tích cực.


Trong C89, mặc dù câu trả lời sẽ luôn là đúng, giá trị chính xác của một phép toán module (họ đề cập đến nó như là phần còn lại) là không xác định, có nghĩa là nó có thể là một kết quả tiêu cực hoặc một kết quả tích cực. Trong C99 kết quả được xác định.

Nếu bạn muốn câu trả lời tích cực, bạn chỉ cần thêm 10 nếu bạn thấy câu trả lời là số âm.

Để có được điều hành modulo để làm việc như nhau trên tất cả các ngôn ngữ, chỉ cần nhớ rằng:

n mod M == (n + M) mod M 

và nói chung:

n mod M == (n + X * M) mod M 
+6

Modulo của số âm trong C ** là ** định nghĩa: bởi tuyên bố sau: * Nếu thương 'a/b' là biểu thức, biểu thức' (a/b) * b + a% b' phải bằng 'a'. * – caf

+3

(tôi nên thêm rằng trong C'% 'không thực sự được định nghĩa là một" modulo "toán tử - nó được định nghĩa là" phần còn lại "). – caf

+1

Có vẻ như vậy, tôi đã đi bởi trang wikipedia này nói rằng nó không được định nghĩa trong C89: http://en.wikipedia.org/wiki/Modulo_operation –

24

Python có hoạt động modulo "đúng", trong khi C có hoạt động còn lại còn lại.

Nó có mối quan hệ trực tiếp với cách xử lý số nguyên âm được xử lý, tức là được làm tròn về 0 hoặc trừ vô hạn. Python tròn về phía trừ vô hạn và C (99) về phía 0, nhưng trong cả hai ngôn ngữ (n/m)*m + n%m == n, do đó toán tử% phải bù theo đúng hướng.

Ada rõ ràng hơn và có cả hai, là modrem.

+0

Lisp thường cũng có cả hai. – Svante

+0

@Svante, Thật dễ dàng để triển khai cả trong thư viện. – Pacerier

+0

@Pacerier: vâng, đó là một hình thức của greenspunning. Modulo và phần còn lại cũng hoạt động ở mức độ rất thấp, do đó, nó không phải là tầm thường để đảm bảo hiệu quả từ một thư viện cấp cao. – Svante

14

Trong C89/90, hành vi của toán tử phân chia và toán tử còn lại với toán hạng âm là được định nghĩa thực hiện, có nghĩa là tùy thuộc vào việc triển khai.Nó chỉ là yêu cầu mà các nhà khai thác đồng ý với nhau: từ a/b = qa % b = r sau a = b * q + r. Sử dụng các xác nhận tĩnh trong mã của bạn để kiểm tra hành vi, nếu nó dựa trên kết quả.

Trong C99, hành vi bạn quan sát đã trở thành tiêu chuẩn.

Thực tế, một trong hai hành vi có logic nhất định trong đó. Hành vi của Python thực hiện hoạt động modulo thực. Hành vi bạn quan sát được là C phù hợp với làm tròn theo hướng 0 (nó cũng là hành vi của Fortran).

Một trong những lý do làm tròn về 0 được ưu tiên trong C là điều tự nhiên là mong đợi kết quả của -a/b giống như -(a/b). Trong trường hợp hành vi modulo thực sự, -1 % 10 sẽ đánh giá là 9, có nghĩa là -1/10 phải bằng -1. Điều này có thể được xem là khá không tự nhiên, vì -(1/10) là 0.

+0

Phân chia toàn bộ số là định kỳ; (a kb)/b = (a/b) + k. Phân chia số thực là định kỳ và đối xứng. Cố gắng xác định phân chia số nguyên để thêm đối xứng sẽ làm cho nó chấm dứt định kỳ trừ khi số chia là số lẻ và được tính bằng cách sử dụng ngữ nghĩa tròn gần nhất. Tôi sẽ coi chu kỳ là quan trọng hơn đối xứng, mặc dù những người khác có thể khác nhau. – supercat

0

Vì python 3.7 bạn cũng có thể sử dụng .remainder() từ mô-đun tích hợp math.

Python 3.7.0a0 (heads/master:f34c685020, May 8 2017, 15:35:30) 
[GCC 4.2.1 Compatible Apple LLVM 8.0.0 (clang-800.0.42.1)] on darwin 
Type "help", "copyright", "credits" or "license" for more information. 
>>> import math 
>>> math.remainder(-1, 10) 
-1.0 

Từ docs:

Return IEEE 754 kiểu còn lại của x đối với y với. Đối với y hữu hạn x và hữu hạn không đồng, đây là sự khác biệt x - n*y, trong đó n là số nguyên gần nhất với giá trị chính xác của thương số x/y. Nếu x/y chính xác nằm giữa hai số nguyên liên tiếp, số nguyên chẵn gần nhất được sử dụng cho n. Phần còn lại r = remainder(x, y) do đó luôn thỏa mãn abs(r) <= 0.5 * abs(y).

Các trường hợp đặc biệt theo IEEE 754: cụ thể, remainder(x, math.inf) là x cho bất kỳ giá trị hữu hạn x, và remainder(x, 0)remainder(math.inf, x) tăng ValueError cho bất kỳ không phải NaN x. Nếu kết quả của phép toán còn lại bằng 0, thì số không đó sẽ có cùng dấu như x.

Trên nền tảng sử dụng điểm nổi nhị phân IEEE 754, kết quả của thao tác này luôn chính xác được thể hiện: không có lỗi làm tròn nào được đưa vào.

0

Chúng ta có thể sử dụng số thập phân Module python để hành xử như C

from decimal import Decimal 
Decimal('-1') % Decimal('10') 

Decimal representation

Các vấn đề liên quan