Question Pourquoi i = i + je me donne 0?


J'ai un programme simple:

public class Mathz {
    static int i = 1;
    public static void main(String[] args) {    
        while (true){
            i = i + i;
            System.out.println(i);
        }
    }
}

Lorsque je lance ce programme, tout ce que je vois est 0 pour i dans ma sortie. J'aurais attendu la première fois que nous aurions i = 1 + 1, suivi par i = 2 + 2, suivi par i = 4 + 4 etc.

Est-ce dû au fait que dès que nous essayons de re-déclarer i du côté gauche, sa valeur est réinitialisée à 0?

Si quelqu'un pouvait m'indiquer les détails les plus fins, ce serait génial.

Changer la int à long et il semble imprimer des chiffres comme prévu. Je suis surpris de la rapidité avec laquelle il atteint la valeur maximale de 32 bits!


95
2018-06-11 22:12


origine


Réponses:


Le problème est dû à un dépassement d'entier.

En arithmétique en complément à 32 bits:

i commence en effet à avoir des valeurs de puissance de deux, mais les comportements de débordement commencent dès que vous atteignez 230:

230 + 230 = -231

-231 + -231 = 0

...dans int arithmétique.


167
2018-06-11 22:14



introduction

Le problème est le dépassement d'entier. S'il déborde, il retourne à la valeur minimale et continue à partir de là. S'il est sous-estimé, il revient à la valeur maximale et continue à partir de là. L'image ci-dessous est un odomètre. Je l'utilise pour expliquer les débordements. C'est un débordement mécanique mais un bon exemple encore.

Dans un odomètre, le max digit = 9, donc aller au-delà des moyens maximum 9 + 1, qui reporte et donne un 0 ; Cependant, il n'y a pas de chiffre plus élevé à changer en 1, le compteur se réinitialise à zero. Vous avez l’idée - les «débordements d’entiers» viennent à l’esprit maintenant.

  enter image description here  enter image description here

Le plus grand littéral décimal de type int est 2147483647 (231-1). Tout   littéraux décimaux de 0 à 2147483647 peuvent apparaître n'importe où un int   littéral peut apparaître, mais le littéral 2147483648 peut apparaître uniquement comme le   opérande de l'opérateur de négation unaire -.

Si un ajout d'entier déborde, le résultat est le plus faible   bits de la somme mathématique telle que représentée dans certains suffisamment grande   format de complément à deux. Si un débordement se produit, alors le signe de la   le résultat n'est pas le même que le signe de la somme mathématique des deux   valeurs d'opérande.

Ainsi, 2147483647 + 1 déborde et passe à -2147483648. Par conséquent int i=2147483647 + 1 serait débordé, ce qui n'est pas égal à 2147483648. En outre, vous dites "il imprime toujours 0". Ce n'est pas parce que http://ideone.com/WHrQIW. Ci-dessous, ces 8 chiffres indiquent le point auquel il pivote et déborde. Il commence alors à imprimer des 0. Aussi, ne soyez pas surpris de la rapidité avec laquelle il calcule, les machines d'aujourd'hui sont rapides.

268435456
536870912
1073741824
-2147483648
0
0
0
0

Pourquoi le débordement d'entier "s'enroule"

  PDF original 


331
2018-06-11 22:23



Non, cela n'imprime que des zéros.

Changez-le pour cela et vous verrez ce qui se passe.

    int k = 50;
    while (true){
        i = i + i;
        System.out.println(i);
        k--;
        if (k<0) break;
    }

Ce qui se passe s'appelle le débordement.


45
2018-06-11 22:14



static int i = 1;
    public static void main(String[] args) throws InterruptedException {
        while (true){
            i = i + i;
            System.out.println(i);
            Thread.sleep(100);
        }
    }

out mis:

2
4
8
16
32
64
...
1073741824
-2147483648
0
0

when sum > Integer.MAX_INT then assign i = 0;

15
2018-06-12 09:22



Comme je n'ai pas assez de réputation, je ne peux pas afficher l'image de la sortie du même programme en C avec une sortie contrôlée, vous pouvez essayer vous-même et voir qu'il imprime 32 fois, puis comme expliqué en raison d'un débordement i = 1073741824 + 1073741824 changements à -2147483648 et une autre addition supplémentaire est hors de portée de int et se tourne vers Zéro .

#include<stdio.h>
#include<conio.h>

int main()
{
static int i = 1;

    while (true){
        i = i + i;
      printf("\n%d",i);
      _getch();
    }
      return 0;
}

4
2018-06-12 14:55



La valeur de i est stocké en mémoire en utilisant une quantité fixe de chiffres binaires. Lorsqu'un nombre nécessite plus de chiffres que ce qui est disponible, seuls les chiffres les plus bas sont stockés (les chiffres les plus élevés sont perdus).

Ajouter i à elle-même est la même chose que de multiplier i par deux Tout comme il est possible de multiplier un nombre par dix en notation décimale en faisant glisser chaque chiffre vers la gauche et en mettant un zéro à droite, il est possible de multiplier un nombre par deux en notation binaire. Cela ajoute un chiffre à droite, ainsi un chiffre est perdu sur la gauche.

Ici, la valeur de départ est 1, donc si nous utilisons 8 chiffres pour stocker i (par exemple),

  • après 0 itération, la valeur est 00000001
  • après 1 itération, la valeur est 00000010
  • après 2 itérations, la valeur est 00000100

et ainsi de suite jusqu'à l'étape finale non nulle

  • après 7 itérations, la valeur est 10000000
  • après 8 itérations, la valeur est 00000000

Quel que soit le nombre de chiffres binaires alloués pour stocker le nombre et quelle que soit la valeur de départ, tous les chiffres seront perdus au fur et à mesure qu'ils seront repoussés vers la gauche. Après ce point, continuer à doubler le nombre ne changera pas le nombre - il sera toujours représenté par tous les zéros.


4
2018-06-12 22:30



C'est correct, mais après 31 itérations, 1073741824 + 1073741824 ne calcule pas correctement et après cela imprime seulement 0.

Vous pouvez refactoriser pour utiliser BigInteger, afin que votre boucle infinie fonctionne correctement.

public class Mathz {
    static BigInteger i = new BigInteger("1");

    public static void main(String[] args) {    

        while (true){
            i = i.add(i);
            System.out.println(i);
        }
    }
}

3
2018-06-11 22:19



Pour déboguer de tels cas, il est bon de réduire le nombre d'itérations dans la boucle. Utilisez ceci au lieu de votre while(true):

for(int r = 0; r<100; r++)

Vous pouvez alors voir qu'il commence par 2 et double la valeur jusqu'à provoquer un débordement.


2
2018-06-11 23:02