Tenho um programa simples:
public class Mathz {
static int i = 1;
public static void main(String[] args) {
while (true){
i = i + i;
System.out.println(i);
}
}
}
Quando executo este programa, tudo o que vejo é 0
a i
minha saída. Eu esperava que a primeira vez que tivéssemos sido i = 1 + 1
, seguido por i = 2 + 2
, seguido por i = 4 + 4
etc.
Isso se deve ao fato de que, assim que tentamos declarar novamente i
no lado esquerdo, seu valor é redefinido para 0
?
Se alguém puder me apontar os detalhes mais sutis disso, seria ótimo.
Altere int
para long
e parece estar imprimindo números conforme o esperado. Estou surpreso com a rapidez com que atinge o valor máximo de 32 bits!
fonte
0
nas primeiras iterações, mas a velocidade de saída está obscurecendo esse fato do OP). Por que isso é aceito?Introdução
O problema é o estouro de inteiro. Se transbordar, ele volta ao valor mínimo e continua a partir daí. Se for insuficiente, ele voltará ao valor máximo e continuará a partir daí. A imagem abaixo é de um odômetro. Eu uso isso para explicar transbordamentos. É um estouro mecânico, mas ainda é um bom exemplo.
Em um odômetro, o
max digit = 9
, portanto, indo além dos meios máximos9 + 1
, que transporta e dá um0
; No entanto, não há dígito superior para alterar para a1
, portanto, o contador é redefinido parazero
. Você começa a idéia - "estouro de inteiros" vêm à mente agora.Assim,
2147483647 + 1
transborda e envolve-2147483648
. Conseqüentemente,int i=2147483647 + 1
seria transbordado, o que não é igual a2147483648
. Além disso, você diz "sempre imprime 0". Não é assim, porque http://ideone.com/WHrQIW . Abaixo, esses 8 números mostram o ponto em que ele gira e transborda. Em seguida, começa a imprimir 0s. Além disso, não se surpreenda com o quão rápido ele calcula, as máquinas de hoje são rápidas.Por que o estouro de inteiro "envolve"
PDF original
fonte
Não, ele não imprime apenas zeros.
Mude para isto e você verá o que acontece.
O que acontece é chamado de estouro.
fonte
true
pori<10000
:)while(k --> 0)
coloquialmente chamado de "enquantok
vai para0
";)resultado:
fonte
Como não tenho reputação suficiente, não posso postar a imagem da saída para o mesmo programa em C com saída controlada, você pode tentar você mesmo e ver que realmente imprime 32 vezes e, em seguida, conforme explicado devido ao estouro i = 1073741824 + 1073741824 muda para -2147483648 e mais uma adição está fora do intervalo de int e muda para Zero.
fonte
system("deltree C:")
, já que você está no DOS / Windows). O estouro de inteiro assinado é um comportamento indefinido em C / C ++, ao contrário de Java. Tenha muito cuidado ao usar esse tipo de construção.signed and unsigned
i += i
por mais de 32 iterações e depois o fezif (i > 0)
. O compilador poderia otimizar isso paraif(true)
já que se estamos sempre adicionando números positivos,i
sempre será maior que 0. Ele também pode deixar a condição em, onde não será executado, por causa do estouro representado aqui. Como o compilador pode produzir dois programas igualmente válidos a partir desse código, é um comportamento indefinido.O valor de
i
é armazenado na memória usando uma quantidade fixa de dígitos binários. Quando um número precisa de mais dígitos do que os disponíveis, apenas os dígitos mais baixos são armazenados (os dígitos mais altos são perdidos).Somando
i
a si mesmo é o mesmo que multiplicari
por dois. Assim como multiplicar um número por dez em notação decimal pode ser executado deslizando cada dígito para a esquerda e colocando um zero à direita, multiplicar um número por dois em notação binária pode ser executado da mesma maneira. Isso adiciona um dígito à direita, de modo que um dígito se perde à esquerda.Aqui, o valor inicial é 1, então, se usarmos 8 dígitos para armazenar
i
(por exemplo),00000001
00000010
00000100
e assim por diante, até a etapa final diferente de zero
10000000
00000000
Não importa quantos dígitos binários são alocados para armazenar o número e não importa qual seja o valor inicial, eventualmente todos os dígitos serão perdidos conforme eles são empurrados para a esquerda. Depois desse ponto, continuar a dobrar o número não mudará o número - ele ainda será representado por zeros.
fonte
Está correto, mas após 31 iterações, 1073741824 + 1073741824 não calcula corretamente e depois disso imprime apenas 0.
Você pode refatorar para usar BigInteger, para que seu loop infinito funcione corretamente.
fonte
int
.long
pode representar números maiores do queint
podem.Para depurar tais casos, é bom reduzir o número de iterações no loop. Use isto em vez de
while(true)
:Você pode ver que ele começa com 2 e está dobrando o valor até causar um estouro.
fonte
Usarei um número de 8 bits para ilustração porque ele pode ser completamente detalhado em um curto espaço. Os números hexadecimais começam com 0x, enquanto os números binários começam com 0b.
O valor máximo para um inteiro sem sinal de 8 bits é 255 (0xFF ou 0b11111111). Se você adicionar 1, normalmente espera obter: 256 (0x100 ou 0b100000000). Mas como são muitos bits (9), isso está acima do máximo, então a primeira parte é descartada, deixando você com 0 efetivamente (0x (1) 00 ou 0b (1) 00000000, mas com o 1 descartado).
Então, quando seu programa é executado, você obtém:
fonte
O maior literal decimal do tipo
int
é 2147483648 (= 2 31 ). Todos os literais decimais de 0 a 2147483647 podem aparecer em qualquer lugar em que um literal int possa aparecer, mas o literal 2147483648 pode aparecer apenas como o operando do operador de negação unário -.Se uma adição inteira estourar, o resultado serão os bits de ordem inferior da soma matemática representada em algum formato de complemento de dois suficientemente grande. Se ocorrer estouro, o sinal do resultado não é o mesmo que o sinal da soma matemática dos dois valores do operando.
fonte