web-dev-qa-db-de.com

Bester Weg, um Javas Modul zu verhalten, wie es sollte mit negativen Zahlen?

In Java, wenn Sie es tun

a % b

Wenn a negativ ist, wird ein negatives Ergebnis zurückgegeben, anstatt b wie gewünscht zu binden. Wie lässt sich das am besten beheben? Ich kann nur so denken

a < 0 ? b + a : a % b
87
fent

Es verhält sich wie es sollte a% b = a - a/b * b; es ist der Rest.

Sie können (a% b + b)% b machen


Dieser Ausdruck funktioniert, da (a % b) notwendigerweise niedriger ist als b, unabhängig davon, ob a positiv oder negativ ist. Durch das Hinzufügen von b werden die negativen Werte von a berücksichtigt, da (a % b) ein negativer Wert zwischen -b und 0 ist. (a % b + b) ist notwendigerweise niedriger als b und positiv. Das letzte Modulo ist für den Fall vorhanden, dass a von Anfang an positiv war, da, wenn a positiv ist, (a % b + b) größer als b werden würde. Daher wird (a % b + b) % b wieder in kleiner als b umgewandelt (und wirkt sich nicht auf negative a-Werte aus).

121
Peter Lawrey

Ab Java 8 können Sie Math.floorMod (int x, int y) und Math.floorMod (long x, long y) verwenden. Beide Methoden liefern die gleichen Ergebnisse wie Peters Antwort.

Math.floorMod( 2,  3) =  2
Math.floorMod(-2,  3) =  1
Math.floorMod( 2, -3) = -1
Math.floorMod(-2, -3) = -2
74
John Krueger

Für diejenigen, die Java 8 noch nicht verwenden (können oder können), kam Guava mit IntMath.mod () zur Hilfe, das seit Guava 11.0 verfügbar ist.

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

Ein Nachteil: Im Gegensatz zu Math.floorMod () von Java 8 kann der Divisor (der zweite Parameter) nicht negativ sein.

9
Ibrahim Arief

In der Zahlentheorie ist das Ergebnis immer positiv. Ich würde vermuten, dass dies in Computersprachen nicht immer der Fall ist, da nicht alle Programmierer Mathematiker sind. Meine zwei Cents würde ich als Designfehler der Sprache betrachten, aber Sie können es jetzt nicht ändern.

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

weil 180 * (-1) + 176 = -4 dasselbe wie 180 * 0 + 176 = 176

Wenn Sie das Uhrbeispiel hier verwenden, ist http://mathworld.wolfram.com/Congruence.html Sie würden nicht sagen, dass duration_of_time mod cycle_length -45 Minuten ist, Sie würden 15 Minuten sagen, auch wenn beide Antworten dem genügen Basisgleichung.

7
Chris Golledge

Java 8 hat Math.floorMod, Ist aber sehr langsam (seine Implementierung hat mehrere Divisionen, Multiplikationen und eine Bedingung). Es ist jedoch möglich, dass die JVM über einen eigenoptimierten Stub verfügt, der sie erheblich beschleunigen würde.

Der schnellste Weg, dies ohne floorMod zu tun, ist wie bei einigen anderen Antworten, jedoch ohne bedingte Verzweigungen und nur mit einem langsamen % Op.

Angenommen, n ist positiv und x kann alles sein:

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;

Die Ergebnisse bei n = 3:

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

Wenn Sie nur eine gleichmäßige Verteilung zwischen 0 Und n-1 Und nicht den genauen Mod-Operator benötigen und Ihre x nicht in der Nähe von 0 Gruppieren, gilt Folgendes Seien Sie noch schneller, da es mehr Parallelität auf Befehlsebene gibt und die langsame % - Berechnung parallel zu den anderen Teilen erfolgt, da sie nicht vom Ergebnis abhängt.

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

Die Ergebnisse für das Obige mit 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

Wenn die Eingabe im gesamten Bereich eines Int zufällig ist, ist die Verteilung beider Lösungen gleich. Wenn die Eingangscluster nahe Null sind, gibt es in der letzteren Lösung zu wenig Ergebnisse bei n - 1.

1
Scott Carey

Hier ist eine Alternative:

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

Dies ist möglicherweise schneller als die andere Formel [(a% b + b)% b], wenn Sie daran denken. Es enthält einen Zweig, der bei modernen Prozessoren normalerweise schlecht ist, aber einen Modulo-Vorgang weniger benötigt.

Eigentlich könnte es definitiv langsamer sein.

(Bearbeiten: Die Formel wurde korrigiert.)

0
Stefan Reich