Comment puis-je vérifier si la multiplication de deux nombres en Java provoque un débordement?

StackOverflow https://stackoverflow.com/questions/1657834

Question

Je veux traiter le cas particulier 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 véritable programme a et b sont source ailleurs à l'exécution. Ce que je veux obtenir quelque chose comme ceci:

long c;
if (a * b will overflow) {
    c = Long.MAX_VALUE;
} else {
    c = a * b;
}

Comment me conseillez-vous meilleur code cela?

Mise à jour: a et b sont toujours non négatif dans mon scénario

.
Était-ce utile?

La solution

Java 8 a Math.multiplyExact, Math.addExact etc. pour ints et longue. Ces jettent un décochée ArithmeticException en cas de débordement.

Autres conseils

Si a et b sont à la fois positifs que vous pouvez ensuite utiliser:

if (a != 0 && b > Long.MAX_VALUE / a) {
    // Overflow
}

Si vous devez traiter avec les deux nombres positifs et négatifs, alors il 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, je fouetté jusqu'à vérifier, 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 existe des bibliothèques Java qui fournissent des opérations arithmétiques de sécurité, qui vérifient à long dépassement haut / bas. Par exemple, de goyave LongMath.checkedMultiply (longtemps, longue b) renvoie le produit de a et b, à condition qu'il ne déborde pas, et jette ArithmeticException si trop-pleins de a * b en arithmétique signé long.

Vous pouvez utiliser java.math.BigInteger au lieu et vérifier la taille du résultat (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 logarithmes pour vérifier la taille du résultat.

Est-ce que 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 Long.MAX_VALUE en question

Volée JRuby

    long result = a * b;
    if (a != 0 && result / a != b) {
       // overflow
    }

MISE À JOUR: Ce code est court et fonctionne bien; cependant, il ne parvient pas à 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 quelques débordements sans division.

Voici la façon la plus simple que 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 ne sais pas pourquoi personne ne cherche à la solution comme:

if (Long.MAX_VALUE/a > b) {
     // overflows
} 

Choisissez un être plus grand des deux nombres.

Je voudrais construire sur la réponse de John Kugelman sans le remplacer en éditant directement. Il travaille pour son cas de test (MIN_VALUE = -10, MAX_VALUE = 10) en raison de la symétrie de MIN_VALUE == -MAX_VALUE, ce qui est le cas pour deux des nombres entiers de complément. 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é à la vraie MIN_VALUE et MAX_VALUE, la réponse de John Kugelman donne un cas de débordement lorsque a == -1 et b == quoi que ce soit d'autre (premier point soulevé par Kyle). Voici une façon 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
}

Il est pas une solution générale pour tout MIN_VALUE et MAX_VALUE, mais il est général pour la Long de Java et Integer et toute valeur de a et b.

Peut-être:

if(b!= 0 && a * b / b != a) //overflow

Je ne sais pas à propos de cette "solution".

Edit:!. Ajouté b = 0

Avant downvote : a * b / b ne sera pas optimisé. Ce serait bug du compilateur. Je ne vois toujours pas un cas où le bogue de débordement peut être masquée.

peut-être 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 il a été souligné, Java 8 a des méthodes Math.xxxExact qui jettent des exceptions en cas de débordement.

Si vous ne l'utilisez Java 8 pour votre projet, vous pouvez « emprunter » leurs implémentations qui sont assez compacts.

Voici quelques liens à ces mises en œuvre sur un site Web 3ème partie, aucune garantie si ceux-ci restent valables mais en tout cas, vous devriez être en mesure d'entrer 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, etc.

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 ne trouve pas 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.save le résultat en gros caractères (int * int mettre le résultat à long, long * longtemps mis à int64)

Résultat 2.cmp >> bits et résultat >> (bits - 1)

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top