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.

87
demandé sur Steve McLeod 2009-11-01 21:02:41

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.

75
répondu bcoughlan 2014-09-24 12:13:01

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
55
répondu John Kugelman 2013-07-16 15:14:17

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.

17
répondu reprogrammer 2018-02-14 11:47:52

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()
}
6
répondu Ulf Lindback 2009-11-01 18:19:39

utilisez les logarithmes pour vérifier la taille du résultat.

6
répondu High Performance Mark 2011-08-30 02:37:24

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

4
répondu nothrow 2009-11-02 11:38:24

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.

3
répondu rogerdpack 2013-07-16 18:37:25

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.

2
répondu fastcodejava 2011-08-30 02:37:32

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 .

2
répondu Jim Pivarski 2015-11-03 18:26:24

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

1
répondu Thomas Jung 2009-11-02 08:22:03

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
répondu dfa 2009-11-02 08:48:57

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.

1
répondu StFS 2016-10-18 15:14:31

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"
}
1
répondu Naren 2017-08-25 02:33:20

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)

-1
répondu xuan 2012-12-05 06:18:17