La meilleure façon de faire le module de Java se comporter comme il devrait avec des nombres négatifs?

en java quand vous faites

a % b

Si a est négatif, il retournera un résultat négatif, au lieu de s'enrouler autour de b comme il devrait. Quelle est la meilleure façon de réparer ça? La seule façon de penser est

a < 0 ? b + a : a % b
81
demandé sur CharlesB 2010-12-10 21:43:56

6 réponses

Il se comporte comme il se doit a % b = a - a / b * b; c'est à dire qu'il est le reste.

Vous pouvez le faire (a % b + b) % b


cette expression fonctionne comme le résultat de (a % b) est nécessairement inférieur à b , peu importe si a est positif ou négatif. L'ajout de b tient compte des valeurs négatives de a , puisque (a % b) est une valeur négative entre -b et 0 , (a % b + b) est nécessairement inférieur à b et positif. Le dernier modulo est là dans le cas a était positif pour commencer, puisque si a est positif (a % b + b) deviendrait plus grand que b . Par conséquent, (a % b + b) % b le transforme en plus petit que b encore une fois (et n'affecte pas les valeurs négatives a ).

118
répondu Peter Lawrey 2014-09-13 10:44:59

à partir de Java 8, Vous pouvez utiliser Math.floorMod (int x, int y) et Math.floorMod (long x, long y) . Ces deux méthodes donnent les mêmes résultats que la réponse de Pierre.

Math.floorMod( 2,  3) =  2
Math.floorMod(-2,  3) =  1
Math.floorMod( 2, -3) = -1
Math.floorMod(-2, -3) = -2
64
répondu John Krueger 2014-09-14 04:45:40

pour ceux qui n'utilisent pas (ou ne peuvent pas utiliser) Java 8 encore, Guava est venu à la rescousse avec IntMath.mod () , disponible depuis Guava 11.0.

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

une mise en garde: contrairement aux maths de Java 8.floorMod (), le diviseur (le second paramètre) ne peut pas être négatif.

8
répondu Ibrahim Arief 2016-03-25 17:10:09

En théorie des nombres, le résultat est toujours positif. Je suppose que ce n'est pas toujours le cas dans les langages informatiques parce que tous les programmeurs ne sont pas mathématiciens. Mes deux cents, je considérerais que c'est un défaut de conception du langage, mais vous ne pouvez pas le changer maintenant.

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

parce que 180 * (-1) + 176 = -4 identique à 180 * 0 + 176 = 176

en utilisant l'exemple de l'horloge ici, http://mathworld.wolfram.com/Congruence.html vous ne diriez pas que la duration_of_time mod cycle_length est de -45 minutes, vous diriez 15 minutes, même si les deux réponses satisfont à l'équation de base.

5
répondu Chris Golledge 2016-06-13 17:21:03

Voici une alternative:

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

cela pourrait ou ne pourrait pas être plus rapide que cette autre formule [(A % b + b) % b], en y repensant. Il contient une branche qui est généralement mal avec les processeurs modernes, mais utilise moins de modulo.

en Fait, il pourrait certainement être plus lent.

(Edit: correction de la formule.)

0
répondu Stefan Reich 2018-08-24 11:15:35

Java 8 a Math.floorMod , mais il est très lent (sa mise en œuvre a plusieurs divisions, multiplications, et un conditionnel). Il est cependant possible que la JVM dispose d'un talon optimisé intrinsèque pour elle, ce qui l'accélérerait considérablement.

la façon la plus rapide de faire ceci sans floorMod est comme quelques autres réponses ici, mais sans branches conditionnelles et seulement un lent % op.

si n est positif, et x peut-être n'importe quoi:

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;

les résultats quand n = 3 :

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

si vous avez seulement besoin d'une distribution uniforme entre 0 et n-1 et non l'opérateur de mod exact, et vos x ne se regroupent pas près de 0 , ce qui suit sera encore plus rapide, car il y a plus de parallélisme de niveau d'instruction et le calcul lent % se produira en parallèle avec les autres parties, car ils ne dépendent pas de son résultat.

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

Les résultats ci-dessus avec 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

Si l'entrée est aléatoire dans la gamme complète de type int, la distribution des deux solutions sera la même. Si les grappes d'entrées sont près de zéro, il y aura trop peu de résultats à n - 1 dans cette dernière solution.

0
répondu Scott Carey 2018-10-01 19:24:07