Negatif sayılarla modulo işlemi


195

Bir C programında aşağıdaki işlemleri deniyordum (Sadece davranışı kontrol etmek için)

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

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

(2, -2 , -2)gcc'deki gibi çıktı verdi . Her seferinde olumlu bir sonuç bekliyordum. Bir modül negatif olabilir mi? Herkes bu davranışı açıklayabilir mi?



Yanıtlar:


170

C99 , temsil edilebilir olduğunda şunları gerektirira/b :

(a/b) * b + a%b eşit olacaka

Bu mantıklı bir şekilde mantıklı. Sağ?

Bunun neye yol açtığını görelim:


Örnek A 5/(-3)olduğu-1

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

Bu ancak 5%(-3)2 olduğunda gerçekleşebilir .


Örnek B. (-5)/3olduğu-1

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

Bu sadece (-5)%3şu durumlarda olabilir-2


1
Derleyici yeterince akıllı olmalı ve imzasız bir modüloun ​​başka bir imzasız olanın her zaman pozitif olduğunu algılamalı mı? Şu anda (iyi, GCC 5.2) derleyici, her iki işlenen uint32_t veya daha büyük olsa bile "%" ifadesinin "imzasız" yerine "int" döndürdüğünü düşünmektedir.
Frederick Nord

@FrederickNord Bu davranışı göstermek için bir örneğiniz var mı?
chux - Monica'yı iade et

10
Açıkladığınız şeyin modun genel int (a / b) (kısaltılmış) açıklaması olduğunu anlayın. Ancak kuralın zemin (a / b) (Knuth) olması da mümkündür. Knuth durumda -5/3olan -2ve mod Kısaca 1 olur: bir modül kar işareti (kesiği), diğer modül bölen işaretini (Knuth) takip eden bir işareti olan, aşağıdaki bir işaret bulunur.
Isaac

1
Bu, C standardının tam olarak istediğim şey olmadığı bir durum. Asla sıfır veya negatif modulo sayılarına kesmek istemedim, ancak çoğu zaman tam tersini istiyorum ve C etrafında çalışmam gerekiyor
Joe

144

%C, operatör değildir modülo operatörü ancak kalan operatörü.

Modulo ve geri kalan operatörler negatif değerlere göre farklılık gösterir.

Geri kalan bir operatörde, sonucun işareti, temettü işareti ile aynıdır, bir modulo operatörü ile, sonucun işareti, bölenle aynıdır.

C %işlemi şu a % bşekilde tanımlar :

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

ile /doğru kesme tamsayı bölümü 0. Bu, bir modulo operatörü yerine kalan bir operatör olarak 0tanımlanan (negatif inifiniteye değil) doğru yapılan kesmedir %.


8
Kalan, tanım gereği modulo işleminin sonucudur . Kalan operatör diye bir şey olmamalıdır, çünkü geri kalan işlem diye bir şey yoktur, buna modulo denir.
gronostaj

41
@gronostaj CS'de değil. Haskell veya Scheme gibi her iki farklı operatörü ( remainderve moduloScheme'de remve modHaskell'de) tanımlayan daha üst düzey dillere bakın . Bu operatörlerin özellikleri, bölümlerin nasıl yapıldığına ilişkin bu diller üzerinde farklılık gösterir: 0'a veya negatif sonsuza doğru kesme. C Standart asla çağırır arada modülo operatörü , sadece o isim % operatörü . %
ouah

2
Bölgedeki IEEE kalıntısını en yakın semantikle uygulayan C'deki remainder fonksiyonla karıştırılmamalıdır
Eric

68

C99 Spesifikasyonuna Göre: a == (a / b) * b + a % b

Hesaplamak için bir fonksiyon yazabiliriz (a % b) == a - (a / b) * b!

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

Modulo işlemi için aşağıdaki fonksiyona sahip olabiliriz (varsayarak b > 0)

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

Benim sonucum, a % bC'de bir modulo işlemi değil, bir kalan işlemdir.


3
Bu b, negatif olduğunda olumlu sonuçlar vermez (ve aslında her ikisi için de rve bdaha az sonuç verir -b). Kullanabileceğiniz tüm girişler için olumlu sonuçlar elde r + abs(b)etmek veya bs işaretini eşleştirmek için durumu r*b < 0bunun yerine değiştirebilirsiniz .
Martin Ender

@MartinEnder r + abs(b)zaman UB olduğunu b == INT_MIN.
chux - Monica'yı

60

Sayının negatif olup olmadığını kontrol etmeye gerek olmadığını düşünmüyorum.

Pozitif modüloyu bulmak için basit bir işlev bu olurdu -

Düzenle: Varsayım N > 0veN + N - 1 <= INT_MAX

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

Bu , x'in hem pozitif hem de negatif değerleri için çalışır .

Orijinal PS: @chux tarafından da belirtildiği gibi, x ve N'niz sırasıyla INT_MAX-1 ve INT_MAX gibi bir şeye ulaşabilirse, intile değiştirin long long int.

Ve eğer onlar da uzun süren sınırları geçiyorlarsa (örn. LLONG_MAX yakınında), pozitif ve negatif vakaları burada diğer cevaplarda açıklandığı gibi ayrı ayrı ele almalısınız.


1
Ne zaman N < 0, sonucun olduğu gibi negatif olabileceğini unutmayın modulo(7, -3) --> -2. Ayrıca tanımsız davranış olan matematik x % N + Ntaşabilir int. örneğin modulo(INT_MAX - 1,INT_MAX)-3 ile sonuçlanabilir.
chux - Monica'yı

Evet, bu durumda long long intnegatif kutuyu ayrı ayrı kullanabilir veya işleyebilirsiniz (basitliği kaybetme pahasına).
Udayraj Deshmukh

9

Diğer cevaplar C99 veya sonrasında açıklanmıştır, negatif işlenenleri içeren tamsayıların bölünmesi her zaman sıfıra doğru kesilir .

İçerisinde, Not C89 sonucu yuvarlak yukarı veya aşağı doğru uygulama tanımlı olsun,. Çünkü (a/b) * b + a%beşittir atüm standartlarda, sonucu %olumsuz işlenen içeren ayrıca uygulama tanımlı C89 içindedir.


5

Bir modül negatif olabilir mi?

%Euclidean_division'dan sonra değil , kalan operatör , bölünme sonrası kalan kısım olduğu için negatif olabilir . C99'dan beri sonuç 0, negatif veya pozitif olabilir.

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

İstenen modulo OP klasik bir Öklid modülo değil %.

Her seferinde olumlu bir sonuç bekliyordum.

Ne zaman iyi tanımlanmış bir Öklid modulo gerçekleştirmek için a/btanımlanır, a,bherhangi bir işaretin olması ve sonucu negatif asla:

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 işleminin sonucu payın işaretine bağlıdır ve bu nedenle y ve z için -2 alırsınız

İşte referans

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

Tamsayı Bölümü

Bu bölümde tamsayı bölme işlevlerini açıklar. Bu işlevler GNU C kütüphanesinde yedeklidir, çünkü GNU C'de '/' operatörü daima sıfıra döner. Ancak diğer C uygulamalarında, '/' negatif argümanlarla farklı şekillerde yuvarlanabilir. div ve ldiv yararlıdır çünkü bölümün nasıl yuvarlanacağını belirtirler: sıfıra doğru. Geri kalan kısım payla aynı işarete sahiptir.


5
ANSI C hakkında bir metne atıfta bulunuyorsunuz. Bu oldukça eski bir C normudur. Metnin ANSI C ile ilgili doğru olup olmadığından emin değilsiniz, ama kesinlikle C99 ile ilgili değil. C99 §6.5.5'te tamsayı bölümü her zaman sıfıra doğru kesilecek şekilde tanımlanır.
Palec

2

Bu sözleşmelerin ortaya çıktığı Matematikte, modulo aritmetiğinin olumlu bir sonuç vermesi gerektiği iddiası yoktur.

Örneğin.

1 mod 5 = 1, ancak aynı zamanda -4'e eşit olabilir. Yani 1/5, 0'dan -4'e veya 5'ten -4'e bir geri kalan 1 verir. (5'in her iki faktörü)

Benzer şekilde, -1 mod 5 = -1, fakat aynı zamanda 4'e eşit olabilir. Yani -1/5, 0'dan 4'e veya -5'ten 4'e bir kalan verir. (Her iki faktör 5)

Daha fazla okumak için için Matematikte denklik sınıflarına bakın.


Eşdeğerlik sınıfı farklı bir kavramdır ve modulo çok katı bir şekilde tanımlanmıştır. En iki tamsayı numaraları var diyelim ave b, b <> 0. Öklid bölünme teoremine göre tam olarak bir çift tamsayı vardır m, rnerede a = m * b + rve 0 <= r < abs( b ). Bahsedilen r(matematiksel) modulo işleminin sonucudur ve tanım gereği negatif değildir. Wikipedia'da daha fazla okuma ve daha fazla bağlantı: en.wikipedia.org/wiki/Euclidean_division
Ister

Bu doğru değil. 1 mod 5her zaman 1. -4 mod 51 de olabilir, ama bunlar farklı şeyler.
FelipeC

2

C99 standardına göre , bölüm 6.5.5 Çarpımsal operatörler , aşağıdakiler gereklidir:

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

Sonuç

C99'a göre, kalan işlemin sonucunun işareti, temettü ile aynıdır.

Hadi bazı örneklere bakalım (dividend / divisor ):

Sadece temettü negatif olduğunda

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

(-3 / 2) * 2 = -2

(-3 % 2) must be -1

Yalnızca bölen negatif olduğunda

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

(3 / -2) * -2 = 2

(3 % -2) must be 1

Hem bölen hem de temettü negatif olduğunda

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

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

(-3 % -2) must be -1

6.5.5 Çarpımsal operatörler

Sözdizimi

  1. çarpımsal ifadesi:
    • cast-expression
    • multiplicative-expression * cast-expression
    • multiplicative-expression / cast-expression
    • multiplicative-expression % cast-expression

Kısıtlamalar

  1. İşlenenlerin her biri aritmetik tipte olacaktır. % Operatörünün işlenenleri tamsayı tipine sahip olacaktır.

semantik

  1. Normal aritmetik dönüşümler işlenenlerde gerçekleştirilir.

  2. İkili * operatörünün sonucu , işlenenlerin ürünüdür.

  3. / Operatörünün sonucu , birinci işlenenin ikinciye bölünmesinden alınan bölümdür; % operatörünün sonucu kalanıdır. Her iki işlemde de, ikinci işlenenin değeri sıfırsa, davranış tanımsızdır.

  4. Tamsayılar bölündüğünde, / operatörünün sonucu, kesirli kısımları atılmış olan cebirsel bölümdür [1]. Bölüm a/btemsil edilebilirse, ifade (a/b)*b + a%beşit olacaktır a.

[1]: Buna genellikle "sıfıra doğru kesme" denir.


1

Modül operatörü geri kalanını verir. C'deki modül operatörü genellikle payın işaretini alır

  1. x =% 5 (-3) - burada pay pozitiftir, dolayısıyla 2 ile sonuçlanır.
  2. y = (-5)% (3) - burada pay negatif olduğundan sonuç -2
  3. z = (-5)% (-3) - burada pay negatif olduğundan sonuç -2

Ayrıca modül (kalan) operatörü sadece tamsayı tipiyle kullanılabilir ve kayan nokta ile kullanılamaz.


2
Bunu harici kaynaklara bağlantılar ile yedekleyebilirseniz iyi olur.
J ... S

1

modSoyut aritmetikte tanımlandığı gibi düşünmenin daha yararlı olduğuna inanıyorum ; bir operasyon olarak değil, farklı unsurlar ve farklı operatörlerle tamamen farklı bir aritmetik sınıfı olarak. Bu,mod 3 işleminin "normal" ekleme ile aynı olmadığı ; yani; tamsayı toplama.

Yani yaptığınız zaman:

5 % -3

5 tamsayısını kümesindeki bir öğeyle eşlemeye çalışıyorsunuz mod -3. Bunlar aşağıdakilerin unsurlarıdır mod -3:

{ 0, -2, -1 }

Yani:

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

Diyelim ki bir sebepten 30 saat kalkmalısın, o günün kaç saatini kaldıracaksın? 30 mod -24.

Ama C'nin uyguladığı şey değil mod, bir kalan. Her neyse, mesele negatifleri geri getirmenin mantıklı olmasıdır.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.