Comment puis-je vérifier si la multiplication de deux nombres en Java causera un débordement?
je veux gérer le cas spécial où la multiplication de deux nombres ensemble provoque un débordement. Le code ressemble à ceci:
int a = 20;
long b = 30;
// if a or b are big enough, this result will silently overflow
long c = a * b;
C'est une version simplifiée. Dans le programme réel a
et b
sont d'autres sources à l'exécution. Ce que je veux accomplir, c'est quelque chose comme ceci:
long c;
if (a * b will overflow) {
c = Long.MAX_VALUE;
} else {
c = a * b;
}
Comment dois-je coder ça?
mise à jour: a
et b
sont toujours non négatif dans mon scénario.
14 réponses
Java 8 a Math.multiplyExact
, Math.addExact
etc. pour les entiers et longue. Ceux-ci jettent un ArithmeticException
non vérifié sur le débordement.
si a
et b
sont tous deux positifs, alors vous pouvez utiliser:
if (a != 0 && b > Long.MAX_VALUE / a) {
// Overflow
}
si vous avez besoin de traiter les nombres positifs et négatifs, alors c'est plus compliqué:
long maximum = Long.signum(a) == Long.signum(b) ? Long.MAX_VALUE : Long.MIN_VALUE;
if (a != 0 && (b > 0 && b > maximum / a ||
b < 0 && b < maximum / a))
{
// Overflow
}
Voici une petite table que j'ai fouettée pour vérifier, en prétendant que le débordement se produit à -10 ou + 10:
a = 5 b = 2 2 > 10 / 5
a = 2 b = 5 5 > 10 / 2
a = -5 b = 2 2 > -10 / -5
a = -2 b = 5 5 > -10 / -2
a = 5 b = -2 -2 < -10 / 5
a = 2 b = -5 -5 < -10 / 2
a = -5 b = -2 -2 < 10 / -5
a = -2 b = -5 -5 < 10 / -2
il y a des bibliothèques Java qui fournissent des opérations arithmétiques sûres, qui vérifient les débordements/sous-écoulements. Par exemple, LongMath De Goyave.checkedMultiply (long a, long b) retourne le produit de a
et b
, à condition qu'il ne déborde pas, et jette ArithmeticException
si a * b
débordements dans signé long
arithmétique.
vous pouvez utiliser java.mathématique.BigInteger à la place et vérifier la taille du résultat (n'ont pas testé le code):
BigInteger bigC = BigInteger.valueOf(a) * multiply(BigInteger.valueOf(b));
if(bigC.compareTo(BigInteger.valueOf(Long.MAX_VALUE)) > 0) {
c = Long.MAX_VALUE;
} else {
c = bigC.longValue()
}
utilisez les logarithmes pour vérifier la taille du résultat.
Java a quelque chose comme int.MaxValue? Si oui, essayez
if (b != 0 && Math.abs(a) > Math.abs(Long.MAX_VALUE / b))
{
// it will overflow
}
edit: vu de Long.MAX_VALUE en question
volé à jruby
long result = a * b;
if (a != 0 && result / a != b) {
// overflow
}
mise à jour: ce code est court et fonctionne bien; cependant, il échoue pour a = -1, b = Long.MIN_VALUE.
Une amélioration possible:
long result = a * b;
if( (Math.signum(a) * Math.signum(b) != Math.signum(result)) ||
(a != 0L && result / a != b)) {
// overflow
}
notez qu'il y aura des débordements sans aucune division.
Je ne sais pas pourquoi personne ne cherche de solution comme:
if (Long.MAX_VALUE/a > b) {
// overflows
}
Choisissez un être plus grand des deux nombres.
j'aimerais construire sur la réponse de John Kugelman sans la remplacer en l'éditant directement. Il fonctionne pour son cas d'essai ( MIN_VALUE = -10
, MAX_VALUE = 10
) en raison de la symétrie de MIN_VALUE == -MAX_VALUE
, qui n'est pas le cas pour les entiers de complément de deux. En réalité, MIN_VALUE == -MAX_VALUE - 1
.
scala> (java.lang.Integer.MIN_VALUE, java.lang.Integer.MAX_VALUE)
res0: (Int, Int) = (-2147483648,2147483647)
scala> (java.lang.Long.MIN_VALUE, java.lang.Long.MAX_VALUE)
res1: (Long, Long) = (-9223372036854775808,9223372036854775807)
appliqué au vrai MIN_VALUE
et MAX_VALUE
, la réponse de John Kugelman donne un cas de débordement quand a == -1
et b ==
autre chose (point premier soulevées par Kyle). Voici un moyen de le corriger:
long maximum = Long.signum(a) == Long.signum(b) ? Long.MAX_VALUE : Long.MIN_VALUE;
if ((a == -1 && b == Long.MIN_VALUE) ||
(a != -1 && a != 0 && ((b > 0 && b > maximum / a) ||
(b < 0 && b < maximum / a))))
{
// Overflow
}
ce n'est pas une solution générale pour les MIN_VALUE
et MAX_VALUE
, mais c'est une solution générale pour les Long
et Integer
de Java et toute valeur de a
et b
.
peut-être:
if(b!= 0 && a * b / b != a) //overflow
pas sûr de cette "solution".
Edit: Ajout de b != 0.
devant vous downvote : a * b / B ne sera pas optimisé. Ce serait un bug de compilateur. Je ne vois toujours pas de cas où le bug de débordement peut être masqué.
peut-être que cela vous aidera:
/**
* @throws ArithmeticException on integer overflow
*/
static long multiply(long a, long b) {
double c = (double) a * b;
long d = a * b;
if ((long) c != d) {
throw new ArithmeticException("int overflow");
} else {
return d;
}
}
comme on l'a fait remarquer, Java 8 a des maths.xxxExact méthodes qui jettent des exceptions sur le débordement.
si vous n'utilisez pas Java 8 pour votre projet, vous pouvez quand même" emprunter " leurs implémentations qui sont assez compactes.
voici quelques liens vers ces implémentations sur un site tiers, aucune garantie que celles-ci resteront valables, mais dans tous les cas vous devriez être en mesure d'aller dans le source JDK et voir comment ils font leur magie à l'intérieur de la java.lang.Math
de la classe.
Math.multiplyExact(long, long)
http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/8u40-b25/java/lang/Math.java?av=f#882
Math.addExact
http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/8u40-b25/java/lang/Math.java?av=f#805
etc, etc.
Voici la façon la plus simple à laquelle je peux penser""
int a = 20;
long b = 30;
long c = a * b;
if(c / b == a) {
// Everything fine.....no overflow
} else {
// Overflow case, because in case of overflow "c/b" can't equal "a"
}
c / c ++ (long * long):
const int64_ w = (int64_) a * (int64_) b;
if ((long) (w >> sizeof(long) * 8) != (long) w >> (sizeof(long) * 8 - 1))
// overflow
java (int * int, désolé, je n'ai pas trouvé int64 en java):
const long w = (long) a * (long) b;
int bits = 32; // int is 32bits in java
if ( (int) (w >> bits) != (int) (w >> (bits - 1))) {
// overflow
}
1.enregistrer le résultat dans une grande type (int*int mettre le résultat à long, long*temps de mettre à int64)
2.résultat cmp > > bits et résultat > > (bits - 1)