การทำงานของโมดูโลที่มีจำนวนลบ


195

ในโปรแกรม C ฉันพยายามดำเนินการด้านล่าง (เพียงเพื่อตรวจสอบพฤติกรรม)

 x = 5 % (-3);
 y = (-5) % (3);
 z = (-5) % (-3); 

printf("%d ,%d ,%d", x, y, z); 

ให้ผลลัพธ์เป็น(2, -2 , -2)gcc ฉันคาดหวังผลลัพธ์ที่เป็นบวกทุกครั้ง โมดูลัสสามารถเป็นลบได้หรือไม่? ใครช่วยอธิบายพฤติกรรมนี้ได้บ้าง


ซ้ำซ้อนที่เป็นไปได้ของstackoverflow.com/questions/4003232/…
james

1
เป็นไปได้ที่ซ้ำกันของตัวดำเนินการ Modulo ที่มีค่าลบ
sugavaneshb

คำตอบ:


170

C99 ต้องการว่าเมื่อใดที่a/bสามารถแสดงได้:

(a/b) * b + a%bจะเท่ากับa

มันสมเหตุสมผลแล้ว ขวา?

มาดูกันว่าสิ่งนี้นำไปสู่:


ตัวอย่าง A. 5/(-3)คือ-1

=> (-1) * (-3) + 5%(-3) =5

สิ่งนี้สามารถเกิดขึ้นได้หาก5%(-3)เป็น 2


ตัวอย่าง B. (-5)/3คือ-1

=> (-1) * 3 + (-5)%3 =-5

สิ่งนี้จะเกิดขึ้นได้(-5)%3ก็ต่อเมื่อเป็น-2


1
คอมไพเลอร์ควรฉลาดพอและตรวจพบว่าโมดูโล่ที่ไม่ได้ลงนามและอื่น ๆ ที่ไม่ได้ลงชื่อเป็นบวกเสมอ? ปัจจุบัน (ดี GCC 5.2) คอมไพเลอร์ดูเหมือนว่าคิดว่า "%" จะส่งกลับ "int" ในกรณีนี้แทนที่จะเป็น "unsigned" แม้ว่าตัวถูกดำเนินการทั้งสองนั้นเป็น uint32_t หรือใหญ่กว่าก็ตาม
Frederick Nord

@FrederickNord คุณมีตัวอย่างที่แสดงพฤติกรรมดังกล่าวหรือไม่?
chux - Reinstate Monica

10
เข้าใจว่าสิ่งที่คุณอธิบายคือคำอธิบายของ mod (a / b) (truncate) ปกติของ mod แต่ก็เป็นไปได้ว่ากฎนั้นเป็นพื้น (a / b) (Knuth) ในกรณี Knuth -5/3คือ-2และ mod จะกลายเป็น 1 โดยย่อ: โมดูลหนึ่งมีสัญญาณที่ตามหลังสัญลักษณ์เงินปันผล (ตัด) โมดูลอื่น ๆ ที่มีเครื่องหมายที่ตามหลังตัวหาร (Knuth)
Isaac

1
นี่เป็นกรณีของมาตรฐาน C ซึ่งไม่ใช่สิ่งที่ฉันต้องการ ฉันไม่เคยต้องการตัดให้เป็นศูนย์หรือโมดูโลตัวเลขลบ แต่บ่อยครั้งต้องการตรงกันข้ามและจำเป็นต้องแก้ไข C.
โจ

144

ตัว%ดำเนินการใน C ไม่ใช่ตัวดำเนินการโมดูโลแต่เป็นตัวดำเนินการส่วนที่เหลือ

โมดูโล่และตัวดำเนินการส่วนที่เหลือแตกต่างกันไปตามค่าลบ

ด้วยตัวดำเนินการส่วนที่เหลือเครื่องหมายของผลลัพธ์จะเหมือนกับเครื่องหมายของการจ่ายเงินปันผลในขณะที่ตัวดำเนินการ modulo นั้นสัญญาณของผลลัพธ์จะเหมือนกับตัวหาร

C กำหนดการ%ดำเนินการสำหรับa % bเป็น:

  a == (a / b * b) + a % b

ด้วยการแบ่งจำนวนเต็มที่มีการตัดต่อ/ 0นั่นคือการตัดทอนที่กระทำต่อ0(และไม่เข้าหาความไม่แน่นอนในเชิงลบ) ที่กำหนดตัว%ดำเนินการส่วนที่เหลือมากกว่าตัวดำเนินการโมดูโล


8
ส่วนที่เหลือเป็นผลมาจากการดำเนินการโมดูโลตามคำนิยาม ไม่ควรมีสิ่งใดเป็นตัวดำเนินการส่วนที่เหลือเพราะไม่มีสิ่งเช่นการดำเนินการส่วนที่เหลือเรียกว่า modulo
gronostaj

41
@ gronostaj ไม่ได้อยู่ใน CS ดูภาษาระดับสูงกว่าเช่น Haskell หรือ Scheme ที่กำหนดทั้งสองตัวดำเนินการที่แตกต่างกัน ( remainderและmoduloใน Scheme remและmodใน Haskell) ข้อกำหนดของโอเปอเรเตอร์เหล่านี้แตกต่างกันในภาษาเหล่านี้เกี่ยวกับวิธีการแบ่ง: การตัดต่อ 0 หรือไปทางอนันต์ลบ โดยวิธีการมาตรฐาน C ไม่เคยเรียกประกอบการโมดูโลพวกเขาเพียงแค่ชื่อมัน ประกอบ% %
ouah

2
เพื่อไม่ให้สับสนกับremainder ฟังก์ชั่นใน C ซึ่งใช้ส่วนที่เหลือของ IEEE ด้วยความหมายแบบรอบต่อใกล้ที่สุดในแผนก
Eric

68

ตามข้อกำหนด C99: a == (a / b) * b + a % b

เราสามารถเขียนฟังก์ชันเพื่อคำนวณได้(a % b) == a - (a / b) * b!

int remainder(int a, int b)
{
    return a - (a / b) * b;
}

สำหรับการทำงานแบบโมดูโลเราสามารถมีฟังก์ชั่นต่อไปนี้ (สมมติว่าb > 0)

int mod(int a, int b)
{
    int r = a % b;
    return r < 0 ? r + b : r;
}

ข้อสรุปของฉันคือa % bใน C เป็นการดำเนินการที่เหลือและไม่ใช่การดำเนินการแบบโมดูโล


3
สิ่งนี้ไม่ได้ให้ผลเชิงบวกเมื่อbเป็นลบ (และในความเป็นจริงสำหรับrและbลบทั้งสองจะให้ผลลัพธ์น้อยกว่า-b) เพื่อให้แน่ใจว่าผลลัพธ์ที่เป็นบวกสำหรับอินพุตทั้งหมดคุณสามารถใช้r + abs(b)หรือจับคู่bเครื่องหมายของคุณสามารถเปลี่ยนเงื่อนไขr*b < 0เป็น
Martin Ender

@MartinEnder r + abs(b)เป็น UB b == INT_MINเมื่อ
chux - Reinstate Monica

60

ฉันไม่คิดว่าไม่จำเป็นต้องตรวจสอบว่าตัวเลขเป็นลบหรือไม่

ฟังก์ชั่นที่ง่ายต่อการค้นหาโมดูโล่เชิงบวกจะเป็นเช่นนี้ -

แก้ไข:สมมติว่าN > 0และN + N - 1 <= INT_MAX

int modulo(int x,int N){
    return (x % N + N) %N;
}

สิ่งนี้จะใช้ได้ทั้งค่าบวกและลบของ x

PS เดิม:ยังเป็นแหลมออกโดย @chux ถ้า x และ N ของคุณอาจเข้าถึงสิ่งที่ต้องการ INT_MAX-1 และ INT_MAX ตามลำดับเพียงแทนที่ด้วยintlong long int

และหากพวกเขาข้ามขีด จำกัด ยาวเช่นกัน (เช่นใกล้ LLONG_MAX) คุณจะต้องจัดการกับกรณีที่เป็นบวกและลบแยกกันตามที่อธิบายไว้ในคำตอบอื่น ๆ ที่นี่


1
ทราบว่าเมื่อผลที่อาจเป็นลบในขณะที่N < 0 modulo(7, -3) --> -2นอกจากนี้ยังx % N + Nสามารถล้นintคณิตศาสตร์ซึ่งเป็นพฤติกรรมที่ไม่ได้กำหนด เช่นmodulo(INT_MAX - 1,INT_MAX)อาจส่งผลให้ -3
chux - Reinstate Monica

ใช่ในกรณีนั้นคุณสามารถใช้long long intหรือจัดการกรณีลบต่างหาก (ในราคาที่เสียความเรียบง่าย)
Udayraj Deshmukh

9

คำตอบอื่น ๆ ได้อธิบายไว้ในC99หรือใหม่กว่าการหารจำนวนเต็มที่เกี่ยวข้องกับตัวถูกดำเนินการเชิงลบจะถูกตัดเป็นศูนย์เสมอ

โปรดสังเกตว่าในC89ว่าผลลัพธ์รอบขึ้นหรือลงนั้นเป็นไปตามที่กำหนดไว้ เนื่องจาก(a/b) * b + a%bเท่ากับaในมาตรฐานทั้งหมดผลของการ%เกี่ยวข้องกับตัวถูกดำเนินการเชิงลบก็กำหนดไว้ใน C89


5

โมดูลัสสามารถเป็นลบได้หรือไม่?

%สามารถเป็นค่าลบมันเป็นผู้ประกอบการที่เหลือที่เหลือหลังจากส่วนที่ไม่ได้หลังจากEuclidean_division ตั้งแต่ C99 ผลลัพธ์อาจเป็น 0, ลบหรือบวก

 // a % b
 7 %  3 -->  1  
 7 % -3 -->  1  
-7 %  3 --> -1  
-7 % -3 --> -1  

โมดูโล OP อยากเป็นคลาสสิกแบบโมดูโลยุคลิด%ไม่

ฉันคาดหวังผลลัพธ์ที่เป็นบวกทุกครั้ง

ในการดำเนินการแบบยุคลิดแบบยุคลิดที่กำหนดไว้อย่างดีเมื่อใดก็ตามที่a/bกำหนดไว้a,bจะมีสัญญาณใด ๆ และผลลัพธ์จะไม่เป็นลบ:

int modulo_Euclidean(int a, int b) {
  int m = a % b;
  if (m < 0) {
    // m += (b < 0) ? -b : b; // avoid this form: it is UB when b == INT_MIN
    m = (b < 0) ? m - b : m + b;
  }
  return m;
}

modulo_Euclidean( 7,  3) -->  1  
modulo_Euclidean( 7, -3) -->  1  
modulo_Euclidean(-7,  3) -->  2  
modulo_Euclidean(-7, -3) -->  2   

2

ผลลัพธ์ของการดำเนินการ Modulo ขึ้นอยู่กับสัญลักษณ์ของตัวเศษดังนั้นคุณจะได้รับ -2 สำหรับyและz

นี่คือการอ้างอิง

http://www.chemie.fu-berlin.de/chemnet/use/info/libc/libc_14.html

กอง Integer

ส่วนนี้อธิบายถึงฟังก์ชั่นสำหรับการดำเนินการหารจำนวนเต็ม ฟังก์ชันเหล่านี้ซ้ำซ้อนในไลบรารี GNU C เนื่องจากใน GNU C ตัวดำเนินการ '/' จะปัดเศษเป็นศูนย์เสมอ แต่ในการใช้งาน C อื่น ๆ '/' อาจปัดเศษด้วยอาร์กิวเมนต์ที่ต่างกัน div และ ldiv มีประโยชน์เพราะพวกเขาระบุวิธีปัดเศษผลหาร: ต่อศูนย์ ส่วนที่เหลือมีเครื่องหมายเดียวกับตัวเศษ


5
คุณกำลังอ้างถึงข้อความเกี่ยวกับ ANSI C นี่เป็นบรรทัดฐานที่ค่อนข้างเก่าของ C. ไม่แน่ใจว่าข้อความนั้นถูกต้องสำหรับ ANSI C หรือไม่ แต่ไม่เกี่ยวข้องกับ C99 ใน C99 §6.5.5การหารจำนวนเต็มถูกกำหนดให้ตัดให้เป็นศูนย์เสมอ
Palec

2

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

เช่น.

1 mod 5 = 1 แต่ก็สามารถเท่ากับ -4 นั่นคือ 1/5 ให้ผลตอบแทนที่เหลือ 1 จาก 0 หรือ -4 จาก 5 (ทั้งสองปัจจัยจาก 5)

ในทำนองเดียวกัน -1 mod 5 = -1 แต่ก็สามารถเท่ากับ 4 นั่นคือ -1/5 ให้ส่วนที่เหลือ -1 จาก 0 หรือ 4 จาก -5 (ทั้งสองปัจจัยจาก 5)

สำหรับการอ่านเพิ่มเติมดูเป็นคลาสเทียบเท่าในคณิตศาสตร์


คลาส Equivalence เป็นแนวคิดที่แตกต่างและโมดูโล่ถูกกำหนดไว้อย่างเข้มงวดมาก สมมติว่าเรามีสองตัวเลขจำนวนเต็มaและ,b b <> 0ตามทฤษฎีบทส่วนยุคลิดมีอยู่ว่าหนึ่งคู่ของจำนวนเต็มm, rที่และa = m * b + r 0 <= r < abs( b )สิ่งที่กล่าวมาrนั้นเป็นผลของการดำเนินการโมดูโล (ทางคณิตศาสตร์) และโดยนิยามไม่ได้เป็นค่าลบ อ่านเพิ่มเติมและลิงค์เพิ่มเติมเกี่ยวกับวิกิพีเดีย: en.wikipedia.org/wiki/Euclidean_division
Ister

นั่นไม่เป็นความจริง 1 mod 5เสมอ 1. -4 mod 5อาจเป็น 1 เช่นกัน แต่สิ่งต่าง ๆ
FelipeC

2

ตามมาตรฐาน C99 , ส่วนที่6.5.5 ตัวดำเนินการแบบ Multiplicativeจำเป็นต้องมีสิ่งต่อไปนี้:

(a / b) * b + a % b = a

ข้อสรุป

เครื่องหมายของผลลัพธ์ของการดำเนินการที่เหลือตาม C99 นั้นเหมือนกับของเงินปันผล

ลองดูตัวอย่าง ( dividend / divisor):

เมื่อเงินปันผลเป็นลบเท่านั้น

(-3 / 2) * 2  +  -3 % 2 = -3

(-3 / 2) * 2 = -2

(-3 % 2) must be -1

เมื่อตัวหารเท่านั้นเป็นลบ

(3 / -2) * -2  +  3 % -2 = 3

(3 / -2) * -2 = 2

(3 % -2) must be 1

เมื่อทั้งตัวหารและเงินปันผลเป็นลบ

(-3 / -2) * -2  +  -3 % -2 = -3

(-3 / -2) * -2 = -2

(-3 % -2) must be -1

6.5.5 ตัวดำเนินการหลายตัว

วากยสัมพันธ์

  1. คูณแสดงออก:
    • cast-expression
    • multiplicative-expression * cast-expression
    • multiplicative-expression / cast-expression
    • multiplicative-expression % cast-expression

ข้อ จำกัด

  1. ตัวถูกดำเนินการแต่ละคนจะต้องมีประเภทเลขคณิต ตัวถูกดำเนินการของตัวดำเนินการ%จะต้องมีชนิดจำนวนเต็ม

อรรถศาสตร์

  1. การแปลงเลขคณิตปกติจะดำเนินการกับตัวถูกดำเนินการ

  2. ผลลัพธ์ของตัวดำเนินการ binary *คือผลผลิตของตัวถูกดำเนินการ

  3. ผลลัพธ์ของตัวดำเนินการ/คือผลหารจากส่วนของตัวถูกดำเนินการแรกโดยที่สอง ผลลัพธ์ของตัวดำเนินการ%คือส่วนที่เหลือ ในการดำเนินการทั้งสองถ้าค่าของตัวถูกดำเนินการที่สองเป็นศูนย์พฤติกรรมจะไม่ได้กำหนด

  4. เมื่อจำนวนเต็มถูกหารผลลัพธ์ของ/โอเปอเรเตอร์คือความฉลาดทางพีชคณิตกับเศษส่วนที่ละทิ้ง [1] หากหารa/bเป็นแทนได้แสดงออกเท่ากับ(a/b)*b + a%ba

[1]: สิ่งนี้มักเรียกว่า "การตัดทอนเข้าหาศูนย์"


1

ตัวดำเนินการโมดูลัสให้ส่วนที่เหลือ ตัวดำเนินการโมดูลัสใน c มักจะใช้สัญลักษณ์ของตัวเศษ

  1. x = 5% (-3) - ที่นี่ตัวเศษเป็นค่าบวกดังนั้นมันจึงให้ผลลัพธ์เป็น 2
  2. y = (-5)% (3) - ตัวเศษนี่คือค่าลบดังนั้นมันจึงเป็นผลลัพธ์ -2
  3. z = (-5)% (-3) - ตัวเศษนี่คือค่าลบดังนั้นจึงเป็นผลลัพธ์ -2

ตัวดำเนินการโมดูลัส (ส่วนที่เหลือ) สามารถใช้ได้กับประเภทจำนวนเต็มเท่านั้นและไม่สามารถใช้กับจุดลอยได้


2
มันจะดีถ้าคุณสามารถสำรองข้อมูลด้วยการเชื่อมโยงไปยังแหล่งข้อมูลภายนอก
J ... S

1

ฉันเชื่อว่ามันมีประโยชน์มากกว่าที่จะคิดmodตามที่นิยามไว้ในเลขคณิตนามธรรม ไม่ได้เป็นการดำเนินการ แต่เป็นคลาสทางคณิตศาสตร์ที่แตกต่างกันโดยสิ้นเชิงกับองค์ประกอบที่แตกต่างกันและผู้ประกอบการที่แตกต่างกัน นั่นหมายถึงการเติมในmod 3นั้นไม่เหมือนกับการเติม "ปกติ" นั่นคือ; การเพิ่มจำนวนเต็ม

ดังนั้นเมื่อคุณ:

5 % -3

คุณพยายามที่จะแมจำนวนเต็ม 5 mod -3ถึงองค์ประกอบในชุดของ นี่คือองค์ประกอบของmod -3:

{ 0, -2, -1 }

ดังนั้น:

0 => 0, 1 => -2, 2 => -1, 3 => 0, 4 => -2, 5 => -1

สมมติว่าคุณต้องตื่นนอนด้วยเหตุผลบางอย่าง 30 ชั่วโมงคุณจะจากไปในวันนั้นกี่ชั่วโมง? 30 mod -24.

แต่สิ่งที่ C ไม่ได้ดำเนินการmodมันเป็นส่วนที่เหลือ อย่างไรก็ตามประเด็นก็คือมันทำให้รู้สึกถึงการกลับเชิงลบ

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