En java quand vous le faites
a % b
Si a est négatif, il renverra un résultat négatif, au lieu de revenir à b comme il se doit. Quelle est la meilleure façon de résoudre ce problème? La seule façon dont je peux penser est
a < 0 ? b + a : a % b
En java quand vous le faites
a % b
Si a est négatif, il renverra un résultat négatif, au lieu de revenir à b comme il se doit. Quelle est la meilleure façon de résoudre ce problème? La seule façon dont je peux penser est
a < 0 ? b + a : a % b
Réponses:
Il se comporte comme il se doit a% b = a - a / b * b; c'est à dire que c'est le reste.
Vous pouvez 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 b
prend en charge les valeurs négatives de a
, puisque (a % b)
est une valeur négative entre -b
et 0
, (a % b + b)
est nécessairement inférieure à b
et positive. Le dernier modulo est là au cas où a
était positif au départ, car si a
est positif (a % b + b)
deviendrait plus grand que b
. Par conséquent, le (a % b + b) % b
transforme en plus petit que de b
nouveau (et n'affecte pas les a
valeurs négatives ).
(a % b)
est nécessairement inférieur à b
(peu importe s'il a
est positif ou négatif), l'addition b
prend en charge les valeurs négatives de a
, car (a % b)
est inférieur à b
et inférieur à 0
, (a % b + b)
est nécessairement inférieur à b
et positif. Le dernier modulo est là au cas où a
était positif au départ, car si a
est positif (a % b + b)
deviendrait plus grand que b
. Par conséquent, le (a % b + b) % b
transforme en plus petit que b
nouveau (et n'affecte pas les a
valeurs négatives ).
a < 0
, peut-être que vous pourriez y jeter un coup d'œil)
(a % b + b) % b
se décompose pour les très grandes valeurs de a
et b
. Par exemple, utiliser a = Integer.MAX_VALUE - 1
et b = Integer.MAX_VALUE
donnera -3
comme résultat, qui est un nombre négatif, ce que vous vouliez éviter.
while
serait plus lent si vous en avez vraiment besoin, sauf que vous n'avez besoin que d'un, if
auquel cas il est en fait plus rapide.
A partir de Java 8, vous pouvez utiliser Math.floorMod (int x, int y) et Math.floorMod (long x, long y) . Ces deux méthodes renvoient les mêmes résultats que la réponse de Peter.
Math.floorMod( 2, 3) = 2
Math.floorMod(-2, 3) = 1
Math.floorMod( 2, -3) = -1
Math.floorMod(-2, -3) = -2
float
ou double
arguments. L'opérateur binaire Mod ( %
) fonctionne également avec les opérandes float
et double
.
Pour ceux qui n'utilisent pas (ou ne peuvent pas encore utiliser) Java 8, 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 à Math.floorMod () de Java 8, le diviseur (le deuxième paramètre) ne peut pas être négatif.
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 car tous les programmeurs ne sont pas des mathématiciens. Mes deux cents, je considérerais cela comme un défaut de conception du langage, mais vous ne pouvez pas le changer maintenant.
= MOD (-4 180) = 176 = MOD (176, 180) = 176
car 180 * (-1) + 176 = -4 identique à 180 * 0 + 176 = 176
En utilisant l'exemple d'horloge ici, http://mathworld.wolfram.com/Congruence.html vous ne diriez pas que 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.
-1
au lieu de, n-1
par exemple) alors ayez à lui.
Java 8 a Math.floorMod
, mais il est très lent (son implémentation a plusieurs divisions, multiplications et un conditionnel). Cependant, il est possible que la JVM dispose d'un stub intrinsèque optimisé, ce qui l'accélérerait considérablement.
Le moyen le plus rapide de faire cela sans floorMod
est comme d'autres réponses ici, mais sans branches conditionnelles et une seule opération lente %
.
En supposant que n est positif et que 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 lorsque 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 que vos x
ne se regroupent pas près 0
, ce qui suit sera encore plus rapide, car il y a plus de parallélisme au niveau des instructions et le %
calcul lent se produira en parallèle avec l 'autre pièces car elles ne dépendent pas de son résultat.
return ((x >> 31) & (n - 1)) + (x % n)
Les résultats pour ce qui précède 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 plage complète d'un entier, la distribution des deux solutions sera la même. Si les grappes d'entrée sont proches de zéro, il y aura trop peu de résultats n - 1
dans cette dernière solution.
Voici une alternative:
a < 0 ? b-1 - (-a-1) % b : a % b
Cela pourrait ou non être plus rapide que cette autre formule [(a% b + b)% b]. Contrairement à l'autre formule, elle contient une branche, mais utilise une opération modulo en moins. Probablement une victoire si l'ordinateur peut prédire correctement un <0.
(Edit: Correction de la formule.)