Pourquoi i = i + je me donne 0?

J’ai un programme simple:

public class Mathz { static int i = 1; public static void main(Ssortingng[] 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 de i = 2 + 2 , suivi de i = 4 + 4 etc.

Est-ce dû au fait que dès que nous essayons de déclarer de nouveau i sur le 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 le int à long et il semble imprimer des nombres comme prévu. Je suis surpris de la vitesse à laquelle il atteint la valeur maximale de 32 bits!

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

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

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

2 30 + 2 30 = -2 31

-2 31 + -2 31 = 0

… en arithmétique.

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 nombre max digit = 9 , donc au-delà des moyennes maximales 9 + 1 , qui reporte et donne un 0 ; Cependant, il n’y a pas de chiffre plus élevé pour passer à 1 , donc le compteur est remis à zero . Vous avez l’idée – les «débordements d’entiers» viennent à l’esprit maintenant.

entrer la description de l'image icientrer la description de l'image ici

Le plus grand littéral décimal de type int est 2147483647 (2 31 -1). Tous les littéraux décimaux compris entre 0 et 2147483647 peuvent apparaître partout où un littéral int peut apparaître, mais le littéral 2147483648 peut apparaître uniquement comme opérande de l’opérateur de négation unaire -.

Si un ajout d’entier déborde, le résultat est les bits de poids faible de la sum mathématique tels que représentés dans un format de complément à deux suffisamment grand. Si un débordement se produit, le signe du résultat n’est pas le même que le signe de la sum 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 0s. 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

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.

 static int i = 1; public static void main(Ssortingng[] 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; 

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’elle imprime 32 fois, puis expliquée en raison d’un débordement i = 1073741824 passe à -2147483648 et une autre addition est hors de scope de int et passe à zéro .

 #include #include int main() { static int i = 1; while (true){ i = i + i; printf("\n%d",i); _getch(); } return 0; } 

La valeur de i est stockée en mémoire en utilisant une quantité fixe de chiffres binarys. 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 à lui-même revient à 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 binary. 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 binarys 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.

C’est correct, mais après 31 itérations, 1073741824 + 1073741824 ne calcule pas correctement et après, n’imprime que 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(Ssortingng[] args) { while (true){ i = i.add(i); System.out.println(i); } } } 

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.

Je vais utiliser un nombre de 8 bits pour illustration car il peut être complètement détaillé dans un espace court. Les nombres hexadécimaux commencent par 0x, tandis que les nombres binarys commencent par 0b.

La valeur maximale pour un entier non signé de 8 bits est 255 (0xFF ou 0b11111111). Si vous ajoutez 1, vous attendez généralement à obtenir: 256 (0x100 ou 0b100000000). Mais comme c’est trop de bits (9), cela dépasse le maximum, donc la première partie est simplement supprimée, vous laissant 0 efficacement (0x (1) 00 ou 0b (1) 00000000, mais avec le 1 abandonné).

Ainsi, lorsque votre programme s’exécute, vous obtenez:

 1 = 0x01 = 0b1 2 = 0x02 = 0b10 4 = 0x04 = 0b100 8 = 0x08 = 0b1000 16 = 0x10 = 0b10000 32 = 0x20 = 0b100000 64 = 0x40 = 0b1000000 128 = 0x80 = 0b10000000 256 = 0x00 = 0b00000000 (wraps to 0) 0 + 0 = 0 = 0x00 = 0b00000000 0 + 0 = 0 = 0x00 = 0b00000000 0 + 0 = 0 = 0x00 = 0b00000000 ... 

Le littéral décimal le plus grand de type int est 2147483648 (= 2 31 ). Tous les littéraux décimaux compris entre 0 et 2147483647 peuvent apparaître partout où un littéral int peut apparaître, mais le littéral 2147483648 peut apparaître uniquement comme opérande de l’opérateur de négation unaire -.

Si un ajout d’entier déborde, le résultat est les bits de poids faible de la sum mathématique tels que représentés dans un format de complément à deux suffisamment grand. Si un débordement se produit, le signe du résultat n’est pas le même que le signe de la sum mathématique des deux valeurs d’opérande.

La raison pour laquelle vous ne voyez pas les nombres les plus bas est que, au moment où il commence à écrire sur la sortie, il a déjà dépassé la limite de int, certains en raison de l’parsing ABCD / JIT en raison de la lenteur de Java …

http://reverseblade.blogspot.com/2009/02/c-versus-c-versus-java-performance.html

Je parie que si vous ajoutiez une barrière de mémoire ou appelez «sleep» ou déclarez le volatile comme volatile vous verriez plus de nombres dans la plage inférieure.