Java modülünün negatif sayılarda olması gerektiği gibi davranmasını sağlamanın en iyi yolu?


103

Javada yaptığın zaman

a % b

Eğer a negatifse, olması gerektiği gibi b'ye sarmak yerine negatif bir sonuç döndürür. Bunu düzeltmenin en iyi yolu nedir? Düşünebilmemin tek yolu

a < 0 ? b + a : a % b

12
Negatif sayılarla uğraşırken "doğru" modül davranışı yoktur - birçok dil bunu bu şekilde yapar, birçok dil farklı yapar ve birkaç dil tamamen farklı bir şey yapar. En azından ilk ikisinin artıları ve eksileri var.

4
bu benim için çok tuhaf. Yalnızca b negatifse negatif dönmesi gerektiğini düşündüm.
fentlik


2
bu. ancak bu sorunun başlığı yeniden adlandırılmalıdır. Bunu arıyor olsaydım bu soruyu tıklamazdım çünkü java modülünün nasıl çalıştığını zaten biliyorum.
fentlik

4
Ben sadece "Neden -% 13 64 = 51?" Şeklinde yeniden adlandırdım, ki bu bir milyon yıl sonra asla birinin arayacağı bir şey olmazdı. Dolayısıyla bu soru başlığı çok daha iyi ve modül, negatif, hesaplama, sayılar gibi anahtar kelimeler üzerinde çok daha fazla aranabilir.
Erick Robertson

Yanıtlar:


144

Olması gerektiği gibi davranır a% b = a - a / b * b; yani geri kalan.

Yapabilirsiniz (a% b + b)% b


Bu ifade, sonucu ister pozitif ister negatif olsun , sonuçtan (a % b)daha düşük olduğu için çalışır . Ekleme negatif değerleri ilgilenir beri arasında negatif bir değerdir ve , zorunlu olarak daha düşük olduğu ve pozitif. Son modulo, başlamak için pozitif olduğu durumda oradadır , çünkü eğer pozitif ise daha büyük olur . Bu nedenle, tekrar olduğundan daha küçük hale getirir (ve negatif değerleri etkilemez ).baba(a % b)-b0(a % b + b)baa(a % b + b)b(a % b + b) % bba


3
bu daha iyi çalışıyor, teşekkürler. ve b'den çok daha büyük olan negatif sayılar için de işe yarar.
fentlik

6
Bu sonucu beri çalışan (a % b)mutlaka daha düşük olduğu b(eğer olursa olsun atoplantıda olumlu veya olumsuz) bnegatif değerleri ilgilenir aberi (a % b)daha düşüktür bve daha düşük 0, (a % b + b)mutlaka daha düşük olduğu bve olumlu. Son modulo, abaşlamak için pozitif olduğu durumda oradadır , çünkü eğer apozitif (a % b + b)ise daha büyük olur b. Bu nedenle, tekrar (a % b + b) % bolduğundan bdaha küçük hale getirir (ve negatif adeğerleri etkilemez ).
ethanfar

1
@eitanfar Mükemmel açıklamanızı cevaba ekledim (küçük bir düzeltmeyle a < 0belki bir göz atabilirsiniz)
Maarten Bodewes

5
Bunun aynı konuyla ilgili başka bir soruya yorum yaptığını gördüm; (a % b + b) % bÇok büyük ave değerleri için bozulduğundan bahsetmeye değer olabilir b. Örneğin, kullanarak a = Integer.MAX_VALUE - 1ve b = Integer.MAX_VALUEverecektir -3kaçınmak istediğini olan negatif bir sayı olduğu sonucu olarak.
Thorbear

2
@Mikepote a kullanmak, whilegerçekten ihtiyacınız varsa daha yavaş olacaktır, ancak yalnızca birine ihtiyacınız vardır if, bu durumda aslında daha hızlıdır.
Peter Lawrey

92

Java 8'den itibaren Math.floorMod (int x, int y) ve Math.floorMod (long x, long y) kullanabilirsiniz . Bu yöntemlerin her ikisi de Peter'ın cevabıyla aynı sonuçları döndürür.

Math.floorMod( 2,  3) =  2
Math.floorMod(-2,  3) =  1
Math.floorMod( 2, -3) = -1
Math.floorMod(-2, -3) = -2

1
Java 8+ için en iyi cevap
Charney Kaye

Harika, bunu bilmiyordum. Java 8, birkaç PITA'yı kesin olarak düzeltti.
Franz D.

4
İyi bir yol. Ama ne yazık ki çalışmaz floatya doubleargümanlar. Mod ikili operatörü ( %) ayrıca floatve doubleişlenenlerle çalışır .
Mir-İsmaili

11

Java 8 kullanmayanlar (veya kullanamayanlar için) Guava, Guava 11.0'dan beri mevcut olan IntMath.mod () ile yardıma geldi .

IntMath.mod( 2, 3) = 2
IntMath.mod(-2, 3) = 1

Bir uyarı: Java 8'in Math.floorMod () 'unun aksine bölen (ikinci parametre) negatif olamaz.


7

Sayı teorisinde sonuç her zaman pozitiftir. Bilgisayar dillerinde bu her zaman geçerli değildir, çünkü tüm programcılar matematikçi değildir. Benim iki sentim, bunu dilin tasarım kusuru olarak değerlendiririm, ama şimdi değiştiremezsin.

= MOD (-4.180) = 176 = MOD (176, 180) = 176

çünkü 180 * (-1) + 176 = -4, 180 * 0 + 176 = 176 ile aynı

Buradaki saat örneğini kullanarak, http://mathworld.wolfram.com/Congruence.html , duration_of_time mod döngüsü_uzunluğu -45 dakika demezsiniz, her iki cevap da temel denklemi karşılasa bile 15 dakika diyebilirsiniz.


1
Sayı teorisinde her zaman olumlu değildir ... Uyum sınıflarına girerler. Notasyon amaçlarınız için o sınıftan herhangi bir adayı seçmekte özgürsünüz, ancak fikir şu ki, tüm sınıfla eşleşiyor ve eğer ondan belirli bir başka adayı kullanmak, belirli bir sorunu önemli ölçüde daha basit hale getiriyorsa ( örneğin -1yerine seçmek n-1) sonra var.
BeUndead

2

Java 8'e sahiptir Math.floorMod, ancak çok yavaştır (uygulamasının birden çok bölümü, çarpımı ve bir koşullu vardır). JVM'nin bunun için kendine özgü optimize edilmiş bir saplamaya sahip olması mümkündür, ancak bu onu önemli ölçüde hızlandıracaktır.

Bunu olmadan yapmanın en hızlı yolu floorModburadaki diğer cevaplar gibidir, ancak koşullu dalları yoktur ve yalnızca bir yavaş %işlem vardır.

N'nin pozitif olduğunu ve x'in herhangi bir şey olabileceğini varsayarsak:

int remainder = (x % n); // may be negative if x is negative
//if remainder is negative, adds n, otherwise adds 0
return ((remainder >> 31) & n) + remainder;

Sonuçlar ne zaman n = 3:

x | result
----------
-4| 2
-3| 0
-2| 1
-1| 2
 0| 0
 1| 1
 2| 2
 3| 0
 4| 1

Tam mod operatörü arasında 0ve arasında tekdüze bir dağılıma ihtiyacınız varsa n-1ve xsizinki yakınlarda kümelenmiyorsa 0, daha fazla komut seviyesi paralelliği olduğundan ve yavaş %hesaplama diğeriyle paralel olarak gerçekleşeceğinden aşağıdakiler daha da hızlı olacaktır. sonucuna bağlı olmadıkları için parçalar.

return ((x >> 31) & (n - 1)) + (x % n)

Yukarıdakiler için sonuçlar n = 3:

x | result
----------
-5| 0
-4| 1
-3| 2
-2| 0
-1| 1
 0| 0
 1| 1
 2| 2
 3| 0
 4| 1
 5| 2

Girdi, bir int'in tüm aralığında rastgele ise, iki çözümün dağılımı aynı olacaktır. Girdi sıfıra yakın kümelenirse n - 1, ikinci çözümde çok az sonuç olacaktır .


1

İşte bir alternatif:

a < 0 ? b-1 - (-a-1) % b : a % b

Bu, diğer formülden [(a% b + b)% b] daha hızlı olabilir veya olmayabilir. Diğer formülden farklı olarak, bir dal içerir, ancak bir daha az modulo işlemi kullanır. Bilgisayar <0'ı doğru tahmin edebiliyorsa, muhtemelen bir kazançtır.

(Düzenleme: Formül düzeltildi.)


1
Ancak modulo işlemi, daha da yavaş olabilecek bir bölme gerektirir (özellikle işlemci şubeyi neredeyse her zaman doğru tahmin ederse). Yani bu muhtemelen daha iyi.
dave

@KarstenR. Haklısın! Formülü düzelttim, şimdi iyi çalışıyor (ancak iki çıkarma daha gerekiyor).
Stefan Reich

Bu doğru @dave
Stefan Reich
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.