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
proviennent d'autres sources lors de l'exécution. Ce que je veux réaliser, c'est quelque chose comme ceci:
long c;
if (a * b will overflow) {
c = Long.MAX_VALUE;
} else {
c = a * b;
}
Comment suggérez-vous que je code le mieux?
Mise à jour: a
et b
sont toujours non négatifs dans mon scénario.
Java 8 a Math.multiplyExact
, Math.addExact
etc. pour les pouces et les longs. Ceux-ci jettent un ArithmeticException
non contrôlé en cas de débordement.
Si a
et b
sont tous deux positifs, vous pouvez utiliser:
if (a != 0 && b > Long.MAX_VALUE / a) {
// Overflow
}
Si vous devez gérer à la fois des nombres positifs et négatifs, 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 un petit tableau que j'ai fouillé pour vérifier cela, en prétendant qu'un 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 existe Java bibliothèques qui fournissent des opérations arithmétiques sûres, qui vérifient les débordements/débordements longs. Par exemple, les goyaves LongMath.checkedMultiply (long a, long b) renvoie le produit de a
et b
, à condition qu'il ne déborde pas, et jette ArithmeticException
si a * b
déborde dans l'arithmétique signée long
.
Vous pouvez utiliser Java.math.BigInteger à la place et vérifier la taille du résultat (vous n'avez 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 des logarithmes pour vérifier la taille du résultat.
Est-ce que Java a quelque chose comme int.MaxValue? Si oui, alors essayez
if (b != 0 && Math.abs(a) > Math.abs(Long.MAX_VALUE / b))
{
// it will overflow
}
edit: vu 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 que cela va attraper certains débordements sans aucune division.
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"
}
Je voudrais m'appuyer sur la réponse de John Kugelman sans la remplacer en la modifiant directement. Cela fonctionne pour son cas de test (MIN_VALUE = -10
, MAX_VALUE = 10
) en raison de la symétrie de MIN_VALUE == -MAX_VALUE
, ce qui n'est pas le cas pour les entiers de complément à 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)
Lorsqu'il est appliqué au vrai MIN_VALUE
et MAX_VALUE
, La réponse de John Kugelman donne un cas de débordement lorsque a == -1
et b ==
autre chose (premier point soulevé par Kyle). Voici un moyen de le réparer:
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 tout MIN_VALUE
et MAX_VALUE
, mais elle est générale pour Long
et Integer
et toutes les valeurs de a
et b
de Java.
Je ne sais pas pourquoi personne ne cherche une solution comme:
if (Long.MAX_VALUE/a > b) {
// overflows
}
Choisissez un pour être plus grand des deux nombres.
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 cela a été souligné, Java 8 a des méthodes Math.xxxExact qui lèvent des exceptions en cas de débordement.
Si vous n'utilisez pas Java 8 pour votre projet, vous pouvez toujours "emprunter" leurs implémentations qui sont assez compactes.
Voici quelques liens vers ces implémentations sur un site Web tiers, aucune garantie si elles resteront valides mais dans tous les cas, vous devriez pouvoir aller dans la source JDK et voir comment ils font leur magie dans la classe Java.lang.Math
.
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.
Peut être:
if(b!= 0 && a * b / b != a) //overflow
Pas sûr de cette "solution".
Edit: Ajouté b! = 0.
Avant de voter: a * b/b ne sera pas optimisé. Ce serait un bug du compilateur. Je ne vois toujours pas de cas où le bug de débordement peut être masqué.