การดำเนินการโมดูโลกับจำนวนลบใน Python


96

ฉันพบพฤติกรรมแปลก ๆ ใน Python เกี่ยวกับจำนวนลบ:

>>> -5 % 4
3

ใครช่วยอธิบายว่าเกิดอะไรขึ้น


25
มองขวาสำหรับฉัน
ข้าวสาลี

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


8
คุณสามารถใช้math.fmodเพื่อให้ได้พฤติกรรมเช่นเดียวกับใน C หรือ Java
0x2b3bfa0

คำตอบ:


137

ต่างจาก C หรือ C ++ ตัวดำเนินการโมดูโลของ Python ( %) จะส่งคืนตัวเลขที่มีเครื่องหมายเดียวกับตัวหาร (ตัวหาร) เสมอ นิพจน์ของคุณให้ 3 เนื่องจาก

(-5) / 4 = -1.25 -> ชั้น (-1.25) = -2

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

ถูกเลือกมากกว่าพฤติกรรม C เนื่องจากผลลัพธ์ที่ไม่เป็นค่าลบมักจะมีประโยชน์มากกว่า ตัวอย่างคือการคำนวณวันในสัปดาห์ ถ้าวันนี้เป็นวันอังคาร (วัน # 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
น่าแปลกที่ผู้ประกอบการโมดูโล ธ (%) ไม่ได้ มักจะกลับมาเป็นจำนวนที่มีเครื่องหมายเดียวกันเป็นตัวหาร (หาร) ดูstackoverflow.com/questions/48347515/…
zezollo

33

นี่คือคำอธิบายจาก Guido van Rossum:

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และพวกเขา ceil r <= 0IFF
Evgeni Sergeev

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

เพราะมันจะทำหน้าที่เช่นเดียวกับการหารปกติและการคูณดังนั้นจึงใช้งานได้ง่ายอย่างสังหรณ์ใจ นั่นอาจไม่สมเหตุสมผลในทางคณิตศาสตร์
Demis

6

ดังที่ได้กล่าวไปแล้ว Python modulo ให้ข้อยกเว้นที่สมเหตุสมผลสำหรับอนุสัญญาของภาษาอื่น ๆ

สิ่งนี้ทำให้ตัวเลขเชิงลบเป็นพฤติกรรมที่ราบรื่นโดยเฉพาะอย่างยิ่งเมื่อใช้ร่วมกับ//ตัวดำเนินการหารจำนวนเต็มเนื่องจาก%โมดูโลมักจะเป็น (เช่นเดียวกับในคณิตศาสตร์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


ขอบคุณตัวอย่างของคุณทำให้ฉันเข้าใจ :)
Lamis

5

ในpythonตัวดำเนินการโมดูโลจะทำงานเช่นนี้

>>> 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

หากท่านต้องการข้อมูลเพิ่มเติมเกี่ยวกับการปัดเศษในหลามอ่านนี้


3

Modulo คลาสเทียบเท่าสำหรับ 4:

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

นี่คือการเชื่อมโยงไปยังพฤติกรรมแบบโมดูโลกับตัวเลขติดลบ (ใช่ฉัน googled มัน)


@NullUserException - ใช่มันเป็น แก้ไขแล้ว. ขอบคุณ.
วีทตี้

1

ฉันยังคิดว่ามันเป็นพฤติกรรมแปลก ๆ ของ Python ปรากฎว่าฉันแก้การหารได้ไม่ดี (บนกระดาษ); ฉันให้ค่า 0 กับผลหารและค่า -5 สำหรับส่วนที่เหลือ แย่มาก ... ฉันลืมการแสดงตัวเลขจำนวนเต็มทางเรขาคณิต เมื่อนึกถึงเรขาคณิตของจำนวนเต็มที่กำหนดโดยเส้นจำนวนเราจะได้ค่าที่ถูกต้องสำหรับผลหารและเศษเหลือและตรวจสอบว่าพฤติกรรมของ Python นั้นใช้ได้ (แม้ว่าฉันคิดว่าคุณได้แก้ไขข้อกังวลของคุณเมื่อนานมาแล้ว)


1

นอกจากนี้ยังควรกล่าวถึงด้วยว่าการแบ่งใน python นั้นแตกต่างจาก C: พิจารณา

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

ใน C คุณคาดหวังผลลัพธ์

0

x / y ใน python คืออะไร?

>>> print x/y
-1

และ% เป็นโมดูโล - ไม่ใช่ส่วนที่เหลือ! ในขณะที่ x% y ใน C ให้ผลตอบแทน

-10

หลามให้ผล

>>> 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 บวกมันจะหลีกเลี่ยงคำสั่งเงื่อนไข

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.