Operasi modulo dengan angka negatif

196

Dalam program C saya mencoba operasi di bawah ini (Hanya untuk memeriksa perilaku)

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

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

memberi saya output seperti (2, -2 , -2)dalam gcc. Saya mengharapkan hasil yang positif setiap saat. Bisakah modulus menjadi negatif? Adakah yang bisa menjelaskan perilaku ini?

Alva
sumber
Kemungkinan duplikat stackoverflow.com/questions/4003232/…
james
1
kemungkinan duplikat dari operator Modulo dengan nilai negatif
sugavaneshb

Jawaban:

170

C99 mensyaratkan bahwa ketika a/brepresentable:

(a/b) * b + a%b harus samaa

Ini masuk akal, secara logis. Baik?

Mari kita lihat apa yang menyebabkan hal ini:


Contoh A. 5/(-3)adalah-1

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

Ini hanya dapat terjadi jika 5%(-3)2.


Contoh B. (-5)/3adalah-1

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

Ini hanya dapat terjadi jika (-5)%3ada-2

ArjunShankar
sumber
1
Haruskah kompiler cukup pintar dan mendeteksi bahwa modulo yang tidak ditandatangani yang lain tidak ditandatangani selalu positif? Saat ini (well, GCC 5.2) kompiler tampaknya berpikir bahwa "%" mengembalikan "int" dalam kasus ini, daripada "tidak ditandatangani" bahkan ketika kedua operan adalah uint32_t atau lebih besar.
Frederick Nord
@FrederickNord Apakah Anda memiliki contoh untuk menunjukkan perilaku itu ?
chux
11
Pahami bahwa apa yang Anda gambarkan adalah deskripsi int (a / b) (truncate) mod yang biasa. Tetapi mungkin juga aturannya adalah floor (a / b) (Knuth). Dalam kasus Knuth -5/3adalah -2dan mod menjadi 1. Singkatnya: satu modul memiliki tanda yang mengikuti tanda dividen (terpotong), modul lainnya memiliki tanda yang mengikuti tanda pembagi (Knuth).
Isaac
1
Ini adalah kasus standar C yang tepatnya bukan yang saya inginkan. Saya tidak pernah ingin memotong ke angka modulo nol atau negatif, tetapi sering ingin yang sebaliknya dan perlu bekerja di sekitar C.
Joe
145

The %operator dalam C bukan modulo Operator tapi sisanya operator.

Modulo dan operator lainnya berbeda dalam hal nilai negatif.

Dengan operator sisa, tanda hasilnya sama dengan tanda dividen sementara dengan operator modulo tanda hasilnya sama dengan pembagi.

C mendefinisikan %operasi untuk a % bsebagai:

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

dengan /divisi integer dengan pemotongan menuju 0. Itu pemotongan yang dilakukan terhadap 0(dan bukan menuju inifinity negatif) yang mendefinisikan %sebagai operator sisa daripada operator modulo.

ouah
sumber
8
Sisa adalah hasil operasi modulo menurut definisi. Seharusnya tidak ada yang namanya operator sisa karena tidak ada yang namanya operasi sisa, itu disebut modulo.
gronostaj
41
@ gronostaj tidak dalam CS. Lihatlah bahasa tingkat yang lebih tinggi seperti Haskell atau Skema yang keduanya mendefinisikan dua operator yang berbeda ( remainderdan modulodalam Skema, remdan moddalam Haskell). Spesifikasi operator ini berbeda pada bahasa ini tentang cara pembagian dilakukan: pemotongan menuju 0 atau ke arah infinity negatif. Dengan cara C Standar tidak pernah menyebut %para operator yang Modulo , mereka hanya nama itu yang Operator% .
ouah
2
Jangan bingung dengan remainder fungsi dalam C, yang mengimplementasikan sisa IEEE dengan semantik bulat-ke-terdekat di divisi
Eric
68

Berdasarkan Spesifikasi C99: a == (a / b) * b + a % b

Kita dapat menulis fungsi untuk menghitung (a % b) == a - (a / b) * b!

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

Untuk operasi modulo, kita dapat memiliki fungsi berikut (dengan asumsi b > 0)

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

Kesimpulan saya adalah bahwa a % bdalam C adalah operasi sisa dan BUKAN operasi modulo.

dewang
sumber
3
Ini tidak memberikan hasil positif ketika bnegatif (dan pada kenyataannya untuk rdan bkeduanya negatif memberikan hasil kurang dari -b). Untuk memastikan hasil positif untuk semua input yang dapat Anda gunakan r + abs(b)atau untuk mencocokkan bdengan tanda, Anda dapat mengubah kondisi r*b < 0.
Martin Ender
@MartinEnder r + abs(b)adalah UB kapan b == INT_MIN.
chux - Reinstate Monica
60

Saya tidak berpikir tidak perlu memeriksa apakah angkanya negatif.

Fungsi sederhana untuk menemukan modulo positif adalah ini -

Edit: Dengan asumsi N > 0danN + N - 1 <= INT_MAX

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

Ini akan bekerja untuk nilai x positif dan negatif .

PS asli: juga seperti yang ditunjukkan oleh @chux, Jika x dan N Anda masing-masing dapat mencapai sesuatu seperti INT_MAX-1 dan INT_MAX, cukup ganti intdengan long long int.

Dan Jika mereka melewati batas yang lama juga (yaitu dekat LLONG_MAX), maka Anda harus menangani kasus positif dan negatif secara terpisah seperti yang dijelaskan dalam jawaban lain di sini.

Udayraj Deshmukh
sumber
1
Perhatikan bahwa ketika N < 0, hasilnya mungkin negatif seperti pada modulo(7, -3) --> -2. Juga x % N + Nbisa meluap intmatematika yang merupakan perilaku yang tidak terdefinisi. misalnya modulo(INT_MAX - 1,INT_MAX)bisa menghasilkan -3.
chux - Reinstate Monica
Ya, dalam hal ini Anda cukup menggunakan long long int, atau menangani kasus negatif secara terpisah (dengan biaya kehilangan kesederhanaan).
Udayraj Deshmukh
9

Jawaban lain telah dijelaskan dalam C99 atau lebih baru, pembagian bilangan bulat yang melibatkan operan negatif selalu terpotong ke nol .

Perhatikan bahwa, dalam C89 , apakah hasil putaran ke atas atau ke bawah ditentukan oleh implementasi. Karena (a/b) * b + a%bsama adi semua standar, hasil dari %melibatkan operan negatif juga implementasi-didefinisikan dalam C89.

Yu Hao
sumber
5

Bisakah modulus menjadi negatif?

%bisa negatif karena ini adalah operator sisa , sisanya setelah pembagian, bukan setelah Euclidean_division . Sejak C99 hasilnya mungkin 0, negatif atau positif.

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

The modulo OP ingin adalah klasik modulo Euclidean , tidak %.

Saya mengharapkan hasil yang positif setiap saat.

Untuk melakukan modulo Euclidean yang terdefinisi dengan baik kapan pun a/bdidefinisikan, a,badalah dari tanda apa pun dan hasilnya tidak pernah negatif:

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   
chux - Pasang kembali Monica
sumber
2

Hasil operasi Modulo tergantung pada tanda pembilang, sehingga Anda mendapatkan -2 untuk y dan z

Inilah rujukannya

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

Divisi Integer

Bagian ini menjelaskan fungsi untuk melakukan pembagian integer. Fungsi-fungsi ini berlebihan di pustaka GNU C, karena di GNU C operator '/' selalu membulatkan ke nol. Tetapi dalam implementasi C lainnya, '/' dapat membulatkan berbeda dengan argumen negatif. div dan ldiv berguna karena mereka menentukan cara membulatkan hasil bagi: menuju nol. Sisanya memiliki tanda yang sama dengan pembilang.

Kartik Anand
sumber
5
Anda merujuk pada teks tentang ANSI C. Ini adalah norma C. yang cukup lama. Tidak yakin apakah teks tersebut benar tentang ANSI C, tetapi jelas tidak berkaitan dengan C99. Dalam C99 §6.5.5 pembagian integer didefinisikan untuk selalu memotong ke nol.
Palec
2

Dalam Matematika, di mana konvensi ini berasal, tidak ada pernyataan bahwa modulo aritmatika harus menghasilkan hasil yang positif.

Misalnya.

1 mod 5 = 1, tetapi bisa juga sama dengan -4. Yaitu, 1/5 menghasilkan sisa 1 dari 0 atau -4 dari 5. (Kedua faktor 5)

Demikian pula, -1 mod 5 = -1, tetapi bisa juga sama dengan 4. Yaitu, -1/5 menghasilkan sisa -1 dari 0 atau 4 dari -5. (Kedua faktor 5)

Untuk bacaan lebih lanjut, lihatlah ke kelas ekivalensi dalam Matematika.

DarkPurple141
sumber
Kelas kesetaraan adalah konsep yang berbeda dan modulo didefinisikan dengan cara yang sangat ketat. Katakanlah kita memiliki dua nomor bilangan bulat adan b, b <> 0. Menurut teorema pembagian Euclidean ada persis sepasang sepasang bilangan bulat m, di rmana a = m * b + rdan 0 <= r < abs( b ). Kata tersebut radalah hasil dari operasi modulo (matematis) dan menurut definisi tidak negatif. Lebih banyak bacaan dan tautan lebih lanjut di Wikipedia: en.wikipedia.org/wiki/Euclidean_division
Ister
Itu tidak benar. 1 mod 5selalu 1. -4 mod 5mungkin 1 juga, tetapi mereka adalah hal yang berbeda.
FelipeC
2

Menurut standar C99 , bagian 6.5.5 Operator penggandaan , yang berikut ini diperlukan:

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

Kesimpulan

Tanda hasil dari operasi sisa, menurut C99, sama dengan tanda dividen.

Mari kita lihat beberapa contoh ( dividend / divisor):

Ketika hanya dividen yang negatif

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

(-3 / 2) * 2 = -2

(-3 % 2) must be -1

Ketika hanya pembagi yang negatif

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

(3 / -2) * -2 = 2

(3 % -2) must be 1

Ketika kedua pembagi dan dividen negatif

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

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

(-3 % -2) must be -1

6.5.5 Operator multiplikasi

Sintaksis

  1. ekspresi multiplikatif:
    • cast-expression
    • multiplicative-expression * cast-expression
    • multiplicative-expression / cast-expression
    • multiplicative-expression % cast-expression

Kendala

  1. Setiap operan harus memiliki tipe aritmatika. Operan dari operator % harus memiliki tipe integer.

Semantik

  1. Konversi aritmatika biasa dilakukan pada operan.

  2. Hasil dari operator biner * adalah produk dari operan.

  3. Hasil dari / operator adalah hasil bagi dari pembagian operan pertama oleh yang kedua; hasil dari operator % adalah sisanya. Dalam kedua operasi, jika nilai operan kedua adalah nol, perilaku tidak terdefinisi.

  4. Ketika bilangan bulat dibagi, hasil / operator adalah hasil bagi aljabar dengan setiap bagian fraksional dibuang [1]. Jika hasil bagi a/bdapat diwakili, ekspresi (a/b)*b + a%bharus sama a.

[1]: Ini sering disebut "pemotongan ke nol".

Ricardo Biehl Pasquali
sumber
1

Operator modulus memberikan sisanya. Operator modulus dalam c biasanya mengambil tanda pembilang

  1. x = 5% (-3) - di sini pembilangnya positif sehingga menghasilkan 2
  2. y = (-5)% (3) - di sini pembilangnya negatif maka hasilnya -2
  3. z = (-5)% (-3) - di sini pembilangnya negatif maka hasilnya -2

Juga operator modulus (sisa) hanya dapat digunakan dengan tipe integer dan tidak dapat digunakan dengan floating point.

Kavya
sumber
2
Alangkah baiknya jika Anda dapat mendukung ini dengan tautan ke sumber daya eksternal.
J ... S
1

Saya percaya ini lebih berguna untuk dipikirkan modkarena didefinisikan dalam aritmatika abstrak; bukan sebagai operasi, tetapi sebagai kelas aritmatika yang berbeda, dengan elemen yang berbeda, dan operator yang berbeda. Itu berarti penambahan dalam mod 3tidak sama dengan penambahan "normal"; itu adalah; penambahan bilangan bulat.

Jadi ketika Anda melakukannya:

5 % -3

Anda mencoba memetakan bilangan bulat 5 ke elemen di set mod -3. Ini adalah elemen dari mod -3:

{ 0, -2, -1 }

Begitu:

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

Katakanlah Anda harus begadang karena sesuatu alasan 30 jam, berapa jam lagi yang tersisa untuk hari itu? 30 mod -24.

Tetapi apa yang tidak diterapkan oleh C mod, itu adalah sisa. Bagaimanapun, intinya adalah masuk akal untuk mengembalikan negatif.

FelipeC
sumber