web-dev-qa-db-fra.com

Comment puis-je vérifier si la multiplication de deux nombres dans Java provoquera 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 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.

92
Steve McLeod

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.

80
bcoughlan

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
59
John Kugelman

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.

17
reprogrammer

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()
}
6
Ulf Lindback

Utilisez des logarithmes pour vérifier la taille du résultat.

6

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

4
nothrow

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.

3
rogerdpack

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"
}
3
Naren

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.

2
Jim Pivarski

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.

2
fastcodejava

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;
    }
}
1
dfa

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.addExacthttp://grepcode.com/file/repository.grepcode.com/Java/root/jdk/openjdk/8u40-b25/Java/lang/Math.java?av=f# 805

etc.

1
StFS

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é.

1
Thomas Jung