Comment Java poignée entier underflows et des débordements et comment voulez-vous vérifier?

comment Java gère-t-il les sous-flux et les débordements d'entier?

à partir de là, comment vérifieriez-vous que cela se produit?

182
demandé sur KushalP 2010-06-09 02:37:16

12 réponses

si elle déborde, elle retourne à la valeur minimale et continue à partir de là. S'il s'écoule, il retourne à la valeur maximale et continue à partir de là.

vous pouvez vérifier cela à l'avance comme suit:

public static boolean willAdditionOverflow(int left, int right) {
    if (right < 0 && right != Integer.MIN_VALUE) {
        return willSubtractionOverflow(left, -right);
    } else {
        return (~(left ^ right) & (left ^ (left + right))) < 0;
    }
}

public static boolean willSubtractionOverflow(int left, int right) {
    if (right < 0) {
        return willAdditionOverflow(left, -right);
    } else {
        return ((left ^ right) & (left ^ (left - right))) < 0;
    }
}

(vous pouvez remplacer int par long pour effectuer les mêmes contrôles pour long )

si vous pensez que cela peut se produire plus que souvent, puis envisager d'utiliser un type de données ou un objet qui peut stocker des valeurs plus grandes, par exemple long ou peut-être java.math.BigInteger . La dernière ne déborde pas, pratiquement, la mémoire JVM disponible est la limite.


si vous êtes déjà sur Java8, alors vous pouvez utiliser les nouvelles méthodes Math#addExact() et Math#subtractExact() qui lancera un ArithmeticException sur overflow.

public static boolean willAdditionOverflow(int left, int right) {
    try {
        Math.addExact(left, right);
        return false;
    } catch (ArithmeticException e) {
        return true;
    }
}

public static boolean willSubtractionOverflow(int left, int right) {
    try {
        Math.subtractExact(left, right);
        return false;
    } catch (ArithmeticException e) {
        return true;
    }
}

le code source se trouve ici et ici respectivement.

bien sûr, vous pouvez aussi les utiliser tout de suite au lieu de les cacher dans une méthode utilitaire boolean .

188
répondu BalusC 2015-05-01 14:42:39

Eh bien, en ce qui concerne les types entiers primitifs, Java ne gère pas du tout les débordements/sous - écoulements (pour float et double le comportement est différent, Il descendra à + / -infini tout comme les mandats IEEE-754).

en ajoutant deux int, vous n'obtiendrez aucune indication quand un débordement se produit. Une méthode simple pour vérifier le dépassement est d'utiliser la prochaine plus grande type pour effectuer l'opération et vérifier si le résultat est toujours à portée pour le type de source:

public int addWithOverflowCheck(int a, int b) {
    // the cast of a is required, to make the + work with long precision,
    // if we just added (a + b) the addition would use int precision and
    // the result would be cast to long afterwards!
    long result = ((long) a) + b;
    if (result > Integer.MAX_VALUE) {
         throw new RuntimeException("Overflow occured");
    } else if (result < Integer.MIN_VALUE) {
         throw new RuntimeException("Underflow occured");
    }
    // at this point we can safely cast back to int, we checked before
    // that the value will be withing int's limits
    return (int) result;
}

ce que vous feriez à la place des clauses de lancer, dépend de vos exigences d'applications (lancer, tirer la chasse à min/max ou tout simplement log whatever). Si vous voulez détecter un débordement lors d'opérations de longue durée, vous n'avez pas de chance avec les primitifs, utilisez plutôt BigInteger.


Edit (2014-05-21): puisque cette question semble être mentionnée assez fréquemment et que j'ai dû résoudre le même problème moi-même, il est assez facile d'évaluer le débordement condition par la même méthode un CPU calculerait son drapeau V.

c'est fondamentalement une expression booléenne qui implique le signe des deux opérandes ainsi que le résultat:

/**
 * Add two int's with overflow detection (r = s + d)
 */
public static int add(final int s, final int d) throws ArithmeticException {
    int r = s + d;
    if (((s & d & ~r) | (~s & ~d & r)) < 0)
        throw new ArithmeticException("int overflow add(" + s + ", " + d + ")");    
    return r;
}

en java, il est plus simple d'appliquer l'expression (dans le si) à la totalité des 32 bits, et de vérifier le résultat en utilisant < 0 (cela permettra effectivement de tester le bit de signe). Le principe fonctionne exactement le même pour tous les types entiers primitifs , changeant tous les déclarations dans la méthode ci-dessus à long le fait fonctionner pendant longtemps.

pour les petits types, en raison de la conversion implicite en int (voir les JLS pour les opérations bitwise pour plus de détails), au lieu de vérifier < 0, le contrôle doit masquer le signe bit explicitement (0x8000 pour les opérandes courtes, 0x80 pour les opérandes byte, ajuster les casts et la déclaration de paramètre appropriée):

/**
 * Subtract two short's with overflow detection (r = d - s)
 */
public static short sub(final short d, final short s) throws ArithmeticException {
    int r = d - s;
    if ((((~s & d & ~r) | (s & ~d & r)) & 0x8000) != 0)
        throw new ArithmeticException("short overflow sub(" + s + ", " + d + ")");
    return (short) r;
}

(notez que dans l'exemple ci-dessus on utilise l'expression need for soustraire détection de débordement)


alors comment/pourquoi ces expressions booléennes fonctionnent-elles? Tout d'abord, une certaine pensée logique révèle qu'un débordement peut seulement se produire si les signes des deux arguments sont les mêmes. Parce que, si un argument est négatif et un positif, le résultat (d'ajouter) doit être plus proche de zéro, ou dans le cas extrême un argument est zéro, le même que l'autre argument. Depuis le arguments par eux-mêmes ne peut pas créer une condition de débordement, leur somme ne peut pas créer un débordement non plus.

que se passe-t-il si les deux arguments ont le même signe? Regardons le cas où les deux sont positifs: ajouter deux arguments qui créent une somme plus grande que les types MAX_VALUE, donnera toujours une valeur négative, donc un débordement se produit si arg1 + arg2 > MAX_VALUE. Maintenant, la valeur maximale qui pourrait en résulter serait MAX_VALUE + MAX_VALUE (le cas extrême les deux arguments sont MAX_VALUE). Pour un octet (exemple) cela signifierait 127 + 127 = 254. En regardant les représentations de bits de toutes les valeurs qui peuvent résulter de l'addition de deux valeurs positives, on trouve que ceux qui débordent (128 à 254) ont tous le bit 7 réglé, tandis que tous ceux qui ne débordent pas (0 à 127) ont le bit 7 (le plus haut, signe) effacé. C'est exactement ce que vérifie la première partie (à droite) de l'expression:

if (((s & d & ~r) | (~s & ~d & r)) < 0)

(~s & ~de d & r) devient vrai, seulement si , les deux opérandes (s, d) sont positives et le résultat (r) est négatif (l'expression fonctionne sur tous les 32 bits, mais le seul bit qui nous intéresse est le bit le plus élevé (signe), qui est coché par le < 0).

maintenant si les deux arguments sont négatifs, leur somme ne peut jamais être plus proche de zéro qu'aucun des arguments, la somme doit être plus proche de moins l'infini. La valeur la plus extrême que nous pouvons produire est MIN_VALUE + MIN_VALUE, qui (encore une fois pour l'exemple byte) montre que pour n'importe quelle valeur dans la gamme (-1 à -128) le bit de signe est réglé, tandis que n'importe quelle valeur de débordement possible (-129 à -256) a le bit de signe effacé. Donc le signe du résultat révèle à nouveau la condition de débordement. C'est ce que la moitié gauche (s & d & ~r) vérifie pour le cas où les deux arguments (s, d) sont négatifs et un résultat qui est positif. La logique est largement équivalente au cas positif; tous les modèles de bits qui peuvent résulter de l'ajout de deux les valeurs négatives auront le bit de signe effacé si et seulement si un sous-flux s'est produit.

60
répondu Durandal 2014-05-21 18:14:32

Java ne fait rien avec le débordement entier pour les types primitifs int ou long et ignore le débordement avec les entiers positifs et négatifs.

cette réponse décrit d'abord le débordement d'entier, donne un exemple de la façon dont il peut se produire, même avec des valeurs intermédiaires dans l'évaluation de l'expression, puis donne des liens vers des ressources qui donnent des techniques détaillées pour prévenir et détecter le débordement d'entier.

arithmétique et les expressions résultant d'un débordement inattendu ou non détecté sont une erreur de programmation courante. Un dépassement d'entier inattendu ou non détecté est également un problème de sécurité exploitable bien connu, en particulier dans la mesure où il affecte les objets array, stack et list.

débordements peuvent se produire dans un sens positif ou négatif, où la valeur positive ou négative serait au-delà de la valeur maximale ou minimale des valeurs pour le type primitif en question. Le débordement peut se produire dans une valeur intermédiaire pendant d'expression ou d'une opération d'évaluation et d'affecter le résultat d'une expression ou d'une opération où la valeur finale devrait être dans la gamme.

parfois, le débordement négatif est appelé, par erreur, "sous-débit". Le sous-flux est ce qui se produit lorsqu'une valeur serait plus proche de zéro que ne le permet la représentation. Le sous-débit se produit en arithmétique entière et est prévu. Le sous-flux entier se produit quand une évaluation entière serait entre -1 et 0 ou 0 et 1. Ce qui serait une résultat fractionnel tronqué à 0. Ceci est normal et attendu avec arithmétique entière et non considéré comme une erreur. Cependant, il peut conduire à code jeter une exception. Un exemple est une exception "ArithmeticException: / by zero" si le résultat de underflow entier est utilisé comme diviseur dans une expression.

considère le code suivant:

int bigValue = Integer.MAX_VALUE;
int x = bigValue * 2 / 5;
int y = bigValue / x;

qui a pour résultat que x se voit attribuer 0 et l'évaluation subséquente de bigValue / X lance un exception, "Exception arithmétique: / par zéro" (c.-à-d. diviser par zéro), au lieu que y se voit attribuer la valeur 2.

le résultat attendu pour x serait 858 993 458, ce qui est inférieur à la valeur int maximale de 2 147 483 647. Cependant, le résultat intermédiaire de l'évaluation entier.MAX_Value * 2, serait 4,294,967,294, ce qui dépasse la valeur int maximale et est -2 selon les représentations integer du complément 2s. L'évaluation ultérieure de -2 / 5 évalue à 0 qui il est assigné à X.

réarrangeant l'expression pour le calcul de x en une expression qui, lorsqu'elle est évaluée, divise avant de multiplier, le code suivant:

int bigValue = Integer.MAX_VALUE;
int x = bigValue / 5 * 2;
int y = bigValue / x;

se traduit par X attribué 858 993 458 et y attribué 2, ce qui est prévu.

le résultat intermédiaire de bigValue / 5 est de 429 496 729 ce qui ne dépasse pas la valeur maximale pour un int. L'évaluation ultérieure de 429 496 729 * 2 ne dépasse pas valeur maximale pour un int et le résultat attendu est attribué à X. L'évaluation pour y ne se divise donc pas par zéro. Les évaluations pour x et y fonctionnent comme prévu.

Java valeurs entières sont stockés au fur et à se comporter en conformité avec 2s de compléter entier signé de représentations. Quand une valeur résultante serait plus grande ou plus petite que les valeurs entières maximum ou minimum, une valeur entière de complément de 2 résulte à la place. Dans des situations qui ne sont pas expressément conçues pour utiliser 2s complement behavior, qui est la plupart des situations arithmétiques entières ordinaires, la valeur 2S complement résultante causera une logique de programmation ou une erreur de calcul comme indiqué dans l'exemple ci-dessus. Un excellent article de Wikipedia décrit des entiers binaires 2S compliment ici: complément de Two-Wikipedia

il y a des techniques pour éviter le débordement involontaire entier. Techinques peuvent être classés comme pré-condition de test, upcasting et BigInteger.

l'essai de condition préalable consiste à examiner les valeurs qui entrent dans une opération ou une expression arithmétique pour s'assurer qu'un débordement ne se produira pas avec ces valeurs. La programmation et la conception devront créer des tests qui garantissent que les valeurs d'entrée ne causeront pas de débordement et détermineront ensuite ce qu'il faut faire si des valeurs d'entrée se produisent qui causeront un débordement.

Upcasting comprend l'utilisation d'un type primitif plus grand pour effectuer l'opération arithmétique ou expression et ensuite déterminer si la valeur obtenue est au-delà des valeurs maximales ou minimales pour un entier. Même avec la mise à jour, il est encore possible que la valeur ou une valeur intermédiaire dans une opération ou une expression soit au-delà des valeurs maximales ou minimales pour le type upcast et cause un débordement, qui ne sera pas non plus détecté et causera des résultats inattendus et indésirables. Par l'analyse ou les conditions préalables, il peut être possible de prévenir le débordement avec upcasting lors de la prévention sans upcasting n'est ni possible ni pratique. Si les entiers en question sont déjà des types primitifs longs, alors upcasting n'est pas possible avec les types primitifs en Java.

la technique BigInteger comprend L'utilisation de BigInteger pour l'opération arithmétique ou l'expression en utilisant des méthodes de bibliothèque qui utilisent BigInteger. BigInteger ne déborde pas. Il utilisera toute la mémoire disponible, si nécessaire. Ses méthodes arithmétiques ne sont normalement que légèrement moins efficace que l'entier opérations. Il est encore possible qu'un résultat utilisant BigInteger soit au-delà des valeurs maximales ou minimales pour un entier, cependant, le débordement ne se produira pas dans l'arithmétique menant au résultat. La programmation et la conception devront encore déterminer ce qu'il faut faire si un résultat BigInteger est au-delà des valeurs maximales ou minimales pour le type de résultat primitif désiré, par exemple, int ou long.

le programme CERT du Carnegie Mellon Software Engineering Institute et Oracle ont créé un ensemble de normes pour la programmation Java sécurisée. Les normes comprennent des techniques de prévention et de détection des débordements d'entiers. La norme est publiée en tant que ressource en ligne librement accessible ici: la norme de codage sécurisé CERT Oracle pour Java

la section de la norme qui décrit et contient des exemples pratiques de techniques de codage pour prévenir ou détecter le débordement d'un nombre entier est ici: NUM00-J. Detect or empêcher le débordement d'entier

Le formulaire

et le formulaire PDF de la norme de codage sécurisé CERT Oracle pour Java sont également disponibles.

30
répondu Jim 2012-07-19 08:49:22

par défaut, les int et long maths de Java s'enroulent silencieusement autour de overflow et underflow. (Les opérations entières sur d'autres types entiers sont effectuées en faisant d'abord la promotion des opérandes à int ou long, par JLS 4.2.2 .)

de Java 8, java.lang.Math fournit addExact , subtractExact , multiplyExact , incrementExact , decrementExact et negateExact méthodes statiques pour les int et long arguments qui effectuent le nom de l'opération, en jetant ArithmeticException sur le dépassement. (Il n'y a pas de méthode de calcul des intérêts intercalaires -- vous devrez vérifier vous-même le cas particulier ( MIN_VALUE / -1 ).)

à partir de Java 8, java.lang.Les mathématiques fournit également toIntExact pour lancer un long à un int, jetant Arithmétiquexception si le long valeur ne tient pas dans un int. Cela peut être utile par exemple pour calculer la somme des ints en utilisant des mathématiques longues non vérifiées, puis en utilisant toIntExact pour lancer à int à la fin (mais attention à ne pas laisser votre somme déborder).

si vous utilisez toujours une ancienne version de Java, Google Guava fournit IntMath et LongMath méthodes statiques pour l'addition vérifiée, la soustraction, la multiplication et l'exponentiation (jeter sur le débordement). Ces classes fournissent également méthodes pour calculer les coefficients factoriels et binomiaux qui retournent MAX_VALUE sur le débordement (ce qui est moins pratique à vérifier). Les classes d'utilité primitives de la goyave, SignedBytes , UnsignedBytes , Shorts et Ints , fournir checkedCast méthodes pour rétrécir les plus grands types (jet IllegalArgumentException sur le sous/débordement, pas ArithmeticException), ainsi que saturatingCast méthodes qui renvoient MIN_VALUE ou MAX_VALUE sur le débordement.

26
répondu Jeffrey Bosboom 2016-03-13 06:58:24

ayant moi-même rencontré ce problème, voici ma solution (à la fois pour la multiplication et l'addition):

static boolean wouldOverflowOccurwhenMultiplying(int a, int b) {
    // If either a or b are Integer.MIN_VALUE, then multiplying by anything other than 0 or 1 will result in overflow
    if (a == 0 || b == 0) {
        return false;
    } else if (a > 0 && b > 0) { // both positive, non zero
        return a > Integer.MAX_VALUE / b;
    } else if (b < 0 && a < 0) { // both negative, non zero
        return a < Integer.MAX_VALUE / b;
    } else { // exactly one of a,b is negative and one is positive, neither are zero
        if (b > 0) { // this last if statements protects against Integer.MIN_VALUE / -1, which in itself causes overflow.
            return a < Integer.MIN_VALUE / b;
        } else { // a > 0
            return b < Integer.MIN_VALUE / a;
        }
    }
}

boolean wouldOverflowOccurWhenAdding(int a, int b) {
    if (a > 0 && b > 0) {
        return a > Integer.MAX_VALUE - b;
    } else if (a < 0 && b < 0) {
        return a < Integer.MIN_VALUE - b;
    }
    return false;
}

n'hésitez pas à corriger si mal ou si peut être simplifiée. J'ai fait quelques tests avec la méthode de multiplication, surtout des cas de bordures, mais ça pourrait quand même être mal.

11
répondu fragorl 2011-11-27 12:29:46

il y a des bibliothèques qui fournissent des opérations arithmétiques sûres, qui vérifient le débordement/le sous-débordement entier . Par exemple, IntMath De Goyave.checkedAdd (int a, int b) retourne la somme de a et b , à condition qu'elle ne déborde pas, et jette ArithmeticException si a + b débours en signé int arithmétique.

8
répondu reprogrammer 2018-03-26 11:47:06

je pense que vous devriez utiliser quelque chose comme ceci et il est appelé Upcasting:

public int multiplyBy2(int x) throws ArithmeticException {
    long result = 2 * (long) x;    
    if (result > Integer.MAX_VALUE || result < Integer.MIN_VALUE){
        throw new ArithmeticException("Integer overflow");
    }
    return (int) result;
}

vous pouvez lire plus ici: Détecter ou de prévenir le débordement d'entier

c'est une source assez fiable.

5
répondu Dusan 2011-07-17 16:25:05

il s'enroule.

E. g:

public class Test {

    public static void main(String[] args) {
        int i = Integer.MAX_VALUE;
        int j = Integer.MIN_VALUE;

        System.out.println(i+1);
        System.out.println(j-1);
    }
}

imprime

-2147483648
2147483647
5
répondu Peter Tillemans 2018-09-09 16:35:01

Il ne fait rien -- le sous/dépassement de capacité se produit juste.

A " -1 "qui est le résultat d'un calcul qui a débordé n'est pas différent du" -1 " qui résulte de toute autre information. Donc, vous ne pouvez pas dire via un certain statut ou en inspectant juste une valeur si elle est débordée.

mais vous pouvez être intelligent au sujet de vos calculs afin d'éviter le débordement, si cela importe, ou au moins savoir quand il se produira. Quelle est votre situation?

3
répondu Sean Owen 2010-06-08 22:43:55
static final int safeAdd(int left, int right)
                 throws ArithmeticException {
  if (right > 0 ? left > Integer.MAX_VALUE - right
                : left < Integer.MIN_VALUE - right) {
    throw new ArithmeticException("Integer overflow");
  }
  return left + right;
}

static final int safeSubtract(int left, int right)
                 throws ArithmeticException {
  if (right > 0 ? left < Integer.MIN_VALUE + right
                : left > Integer.MAX_VALUE + right) {
    throw new ArithmeticException("Integer overflow");
  }
  return left - right;
}

static final int safeMultiply(int left, int right)
                 throws ArithmeticException {
  if (right > 0 ? left > Integer.MAX_VALUE/right
                  || left < Integer.MIN_VALUE/right
                : (right < -1 ? left > Integer.MIN_VALUE/right
                                || left < Integer.MAX_VALUE/right
                              : right == -1
                                && left == Integer.MIN_VALUE) ) {
    throw new ArithmeticException("Integer overflow");
  }
  return left * right;
}

static final int safeDivide(int left, int right)
                 throws ArithmeticException {
  if ((left == Integer.MIN_VALUE) && (right == -1)) {
    throw new ArithmeticException("Integer overflow");
  }
  return left / right;
}

static final int safeNegate(int a) throws ArithmeticException {
  if (a == Integer.MIN_VALUE) {
    throw new ArithmeticException("Integer overflow");
  }
  return -a;
}
static final int safeAbs(int a) throws ArithmeticException {
  if (a == Integer.MIN_VALUE) {
    throw new ArithmeticException("Integer overflow");
  }
  return Math.abs(a);
}
0
répondu user4267316 2014-11-18 20:56:07

il y a un cas, qui n'est pas mentionné ci-dessus:

int res = 1;
while (res != 0) {
    res *= 2;

}
System.out.println(res);

produira:

0

cette affaire a été examinée ici: débordement d'Entier produit de Zéro.

0
répondu lobzik 2017-05-23 12:03:02

je pense que ça devrait aller.

static boolean addWillOverFlow(int a, int b) {
    return (Integer.signum(a) == Integer.signum(b)) && 
            (Integer.signum(a) != Integer.signum(a+b)); 
}
0
répondu John Woo 2016-08-28 03:06:30