110 votes

Comment puis-je vérifier si la multiplication de deux nombres en Java provoquera un dépassement de capacité ?

Je veux gérer le cas particulier où la multiplication de deux nombres provoque un débordement. Le code ressemble à quelque chose comme 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 vrai programme a y b proviennent d'autres sources au moment de l'exécution. Ce que je veux réaliser est quelque chose comme ceci :

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

Quelle est la meilleure façon de coder cela ?

Mise à jour : a y b sont toujours non négatives dans mon scénario.

7 votes

C'est dommage que Java ne fournisse pas d'accès indirect au CPU. drapeau de débordement comme se fait en C# .

61voto

John Kugelman Points 108754

Si a y b sont toutes deux positives, alors vous pouvez utiliser :

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

Si vous devez traiter 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 préparé pour vérifier cela, en prétendant que le dépassement 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

0 votes

Je dois mentionner que a et b sont toujours non négatifs dans mon scénario, ce qui simplifierait quelque peu cette approche.

3 votes

Je pense que cela peut échouer dans un cas : a = -1 et b = 10. L'expression maximum / a a pour résultat Integer.MIN_VALUE et détecte un débordement alors qu'il n'y en avait pas.

0 votes

C'est vraiment bien. Pour ceux qui se demandent, la raison pour laquelle cela fonctionne est que pour les entiers n , n > x est la même chose que n > floor(x) . Pour les nombres entiers positifs, la division effectue un plancher implicite. (Pour les nombres négatifs, elle arrondit à la hausse).

18voto

reprogrammer Points 4831

Il existe des bibliothèques Java qui fournissent des opérations arithmétiques sûres, qui vérifient les débordements et les sous-exécutions de longue durée. Par exemple, la bibliothèque Guava LongMath.checkedMultiply(long a, long b) renvoie le produit de a y b à condition qu'il ne déborde pas, et lance ArithmeticException si a * b les débordements dans les produits signés long arithmétique.

5 votes

Voici la meilleure réponse : utilisez une bibliothèque qui a été implémentée par des personnes qui comprennent vraiment l'arithmétique des machines en Java et qui a été testée par de nombreuses personnes. N'essayez pas d'écrire votre propre code ou d'utiliser l'un des codes non testés affichés dans les autres réponses !

0 votes

@Enerccio -- Je ne comprends pas votre commentaire. Dites-vous que Guava ne fonctionnera pas sur tous les systèmes ? Je peux vous assurer qu'il fonctionnera partout où Java fonctionne. Dites-vous que la réutilisation du code est une mauvaise idée en général ? Si c'est le cas, je ne suis pas d'accord.

2 votes

@Rich Je dis qu'inclure une énorme bibliothèque pour pouvoir utiliser une seule fonction est une mauvaise idée.

6voto

Ulf Lindback Points 5235

Vous pourriez utiliser java.math.BigInteger à la place et vérifier la taille du résultat (je n'ai 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()
}

7 votes

Je trouve cette solution plutôt lente

0 votes

C'est probablement la meilleure façon de le faire, cependant. J'ai supposé qu'il s'agissait d'une application numérique, c'est pourquoi je ne l'ai pas recommandée d'emblée, mais c'est probablement la meilleure façon de résoudre ce problème.

2 votes

Je ne suis pas sûr que vous puissiez utiliser l'opérateur '>' avec BigInteger. Il faut utiliser la méthode compareTo.

5voto

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

0 votes

Voulez-vous dire : ceil(log(a)) + ceil(log(b)) > log(Long.MAX) ?

1 votes

Je l'ai vérifié. Pour les petites valeurs, il est 20% plus rapide que BigInteger et pour les valeurs proches de MAX, il est presque le même (5% plus rapide). Le code de Yossarian est le plus rapide (95% et 75% plus rapide que BigInteger).

0 votes

Je soupçonne plutôt qu'elle peut échouer dans certains cas.

4voto

Yossarian Points 8601

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

0 votes

Je ne t'ai pas descendu mais Math.Abs(a) ne fonctionne pas si a es Long.MIN_VALUE .

0 votes

@John - a et b sont > 0. Je pense que l'approche de Yossarian (b != 0 && a > Long.MAX_VALUE / b) est la meilleure.

0 votes

@Thomas, a et b sont >= 0, c'est-à-dire non négatifs.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X