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. Je me serais attendu au premier tour 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
à gauche, sa valeur est réinitialisée à 0
?
Si quelqu'un peut m'indiquer les détails les plus fins, ce serait formidable.
Remplacez int
par long
et il semble que les chiffres soient imprimés comme prévu. Je suis surpris de la rapidité avec laquelle il atteint la valeur maximale de 32 bits!
Le problème est dû à un dépassement d'entier.
En arithmétique 32 bits à deux compléments:
i
commence bien à 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
... en int
en arithmétique, puisqu'il s'agit essentiellement de l'arithmétique mod 2 ^ 32.
Le problème est un dépassement d'entier. S'il déborde, il retourne à la valeur minimale et continue à partir de là. Si le débit est insuffisant, il revient à la valeur maximale et continue à partir de là. L'image ci-dessous est d'un odomètre. J'utilise ceci pour expliquer les débordements. C'est un débordement mécanique mais un bon exemple quand même.
Dans un compteur kilométrique, le max digit = 9
, donc au-delà du maximum signifie 9 + 1
, qui reporte et donne un 0
; Cependant, il n'y a pas de chiffre plus élevé à changer en 1
, le compteur est donc réinitialisé à zero
. Vous avez l’idée: des "débordements d’entiers" viennent à l’esprit maintenant.
Le plus grand littéral décimal de type int est 2147483647 (231-1). Tous les littéraux décimaux compris entre 0 et 2147483647 peuvent apparaître n'importe où un littéral int peut apparaître, mais le littéral 2147483648 peut apparaître uniquement comme l'opérande de l'opérateur de négation unaire -.
Si une addition entière déborde, le résultat est alors les bits de poids faible de la somme mathématique tels qu'ils sont représentés dans un format suffisamment grand du complément à deux. En cas de dépassement, le signe du résultat n'est pas le même que celui de la somme mathématique des deux valeurs d'opérande.
Ainsi, 2147483647 + 1
déborde et s’enroule autour de -2147483648
. Par conséquent int i=2147483647 + 1
_ serait débordé, ce qui n’est pas égal à 2147483648
. En outre, vous dites "il affiche toujours 0". Ce n'est pas le cas, car http://ideone.com/WHrQIW. Ci-dessous, ces 8 chiffres indiquent le point où il pivote et déborde. Il commence ensuite à 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
Non, il n’imprime pas que des zéros.
Changez-le en ceci 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 débordement.
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;
La valeur de i
est stockée dans la mémoire en utilisant un nombre 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
à lui-même équivaut à 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 de la même manière. Cela ajoute un chiffre à droite, ainsi un chiffre est perdu à gauche.
Ici, la valeur de départ est 1, donc si nous utilisons 8 chiffres pour stocker i
(par exemple),
00000001
00000010
00000100
et ainsi de suite, jusqu'à l'étape finale non nulle
10000000
00000000
Peu importe le nombre de chiffres binaires alloués pour stocker le numéro et quelle que soit la valeur de départ, tous les chiffres seront éventuellement perdus s'ils sont déplacés vers la gauche. Après cela, continuer à doubler le nombre ne changera pas le nombre - il sera toujours représenté par tous les zéros.
Comme je n’ai pas assez de réputation, je ne peux pas poster 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 s’imprime réellement 32 fois, puis comme expliqué en raison du débordement. i = 1073741824 + 1073741824 changements à -2147483648 et un autre ajout est hors de portée de int et se tourne vers Zero .
#include<stdio.h>
#include<conio.h>
int main()
{
static int i = 1;
while (true){
i = i + i;
printf("\n%d",i);
_getch();
}
return 0;
}
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(String[] args) {
while (true){
i = i.add(i);
System.out.println(i);
}
}
}
Je vais utiliser un nombre de 8 bits pour l'illustration, car il peut être complètement détaillé dans un espace court. Les nombres hexadécimaux commencent par 0x, tandis que les nombres binaires commencent par 0b.
La valeur maximale pour un entier non signé de 8 bits est 255 (0xFF ou 0b11111111). Si vous ajoutez 1, vous vous attendez généralement à obtenir: 256 (0x100 ou 0b100000000). Mais comme c'est trop de bits (9), c'est trop, donc la première partie est simplement supprimée, ce qui vous laisse effectivement avec 0 (0x (1) 00 ou 0b (1) 00000000, mais avec le 1 supprimé).
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
...
Pour déboguer de tels cas, il est bon de réduire le nombre d'itérations dans la boucle. Utilisez ceci à la place 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.
Le plus grand littéral décimal de type int
est 2147483648 (= 231) Tous les littéraux décimaux de 0 à 2147483647 peut apparaître n'importe où un littéral int peut apparaître, mais le littéral 2147483648 peut apparaître uniquement comme l'opérande de l'opérateur de négation unaire -.
Si une addition entière déborde, le résultat est alors les bits de poids faible de la somme mathématique tels qu'ils sont représentés dans un format suffisamment grand du complément à deux. En cas de dépassement, le signe du résultat n'est pas le même que celui de la somme mathématique des deux valeurs d'opérande.