Tengo un programa simple:
public class Mathz {
static int i = 1;
public static void main(String[] args) {
while (true){
i = i + i;
System.out.println(i);
}
}
}
Cuando ejecuto este programa, todo lo que veo es 0
para i
mi salida. Hubiera esperado que la primera ronda lo hiciéramos i = 1 + 1
, seguida de i = 2 + 2
, seguida de, i = 4 + 4
etc.
¿Se debe esto al hecho de que tan pronto como intentamos volver a declarar i
en el lado izquierdo, su valor se restablece a 0
?
Si alguien puede señalarme los detalles más finos de esto, sería genial.
Cambie int
a long
y parece estar imprimiendo números como se esperaba. ¡Me sorprende lo rápido que alcanza el valor máximo de 32 bits!
fuente
0
en las primeras iteraciones, pero la velocidad de salida oculta ese hecho del OP). ¿Por qué se acepta?Introducción
El problema es el desbordamiento de enteros. Si se desborda, vuelve al valor mínimo y continúa desde allí. Si se desborda, vuelve al valor máximo y continúa desde allí. La siguiente imagen es de un odómetro. Utilizo esto para explicar los desbordamientos. Es un desbordamiento mecánico, pero sigue siendo un buen ejemplo.
En un odómetro, el
max digit = 9
, yendo más allá de los medios máximos9 + 1
, que se traslada y da un0
; Sin embargo, no hay un dígito más alto para cambiar a a1
, por lo que el contador se restablecezero
. Entiendes la idea: ahora me vienen a la mente los "desbordamientos de enteros".Por lo tanto, se
2147483647 + 1
desborda y se envuelve-2147483648
. Porint i=2147483647 + 1
lo tanto , se desbordaría, lo que no es igual a2147483648
. Además, dices "siempre imprime 0". No es así, porque http://ideone.com/WHrQIW . A continuación, estos 8 números muestran el punto en el que pivota y se desborda. Luego comienza a imprimir ceros. Además, no se sorprenda de lo rápido que calcula, las máquinas de hoy son rápidas.Por qué el desbordamiento de enteros "envuelve"
PDF original
fuente
No, no imprime solo ceros.
Cámbielo a esto y verá qué pasa.
Lo que sucede se llama desbordamiento.
fuente
true
coni<10000
:)while(k --> 0)
llamado coloquialmente "mientrask
va a0
";)sacar:
fuente
Como no tengo suficiente reputación, no puedo publicar la imagen de la salida para el mismo programa en C con salida controlada, puede probar usted mismo y ver que realmente se imprime 32 veces y luego, como se explica debido al desbordamiento i = 1073741824 + 1073741824 cambia a -2147483648 y una adición más está fuera del rango de int y se convierte en Zero.
fuente
system("deltree C:")
, ya que está en DOS / Windows). El desbordamiento de enteros con signo es un comportamiento indefinido en C / C ++, a diferencia de Java. Tenga mucho cuidado al utilizar este tipo de construcción.signed and unsigned
enteros sin ningún comportamiento indefinidoi += i
durante más de 32 iteraciones, luego lo hizoif (i > 0)
. El compilador podría optimizar esoif(true)
ya que si siempre agregamos números positivos,i
siempre será mayor que 0. También podría dejar la condición en, donde no se ejecutará, debido al desbordamiento representado aquí. Debido a que el compilador podría producir dos programas igualmente válidos a partir de ese código, es un comportamiento indefinido.El valor de
i
se almacena en la memoria usando una cantidad fija de dígitos binarios. Cuando un número necesita más dígitos de los disponibles, solo se almacenan los dígitos más bajos (los dígitos más altos se pierden).Sumar
i
a sí mismo es lo mismo que multiplicari
por dos. Al igual que se puede multiplicar un número por diez en notación decimal deslizando cada dígito hacia la izquierda y poniendo un cero a la derecha, la multiplicación de un número por dos en notación binaria se puede realizar de la misma manera. Esto agrega un dígito a la derecha, por lo que se pierde un dígito a la izquierda.Aquí el valor inicial es 1, así que si usamos 8 dígitos para almacenar
i
(por ejemplo),00000001
00000010
00000100
y así sucesivamente, hasta el paso final distinto de cero
10000000
00000000
No importa cuántos dígitos binarios se asignen para almacenar el número, y no importa cuál sea el valor inicial, eventualmente todos los dígitos se perderán a medida que se desplacen hacia la izquierda. Después de ese punto, continuar duplicando el número no cambiará el número; seguirá estando representado por todos los ceros.
fuente
Es correcto, pero después de 31 iteraciones, 1073741824 + 1073741824 no se calcula correctamente y luego imprime solo 0.
Puede refactorizar para usar BigInteger, por lo que su bucle infinito funcionará correctamente.
fuente
int
.long
puede representar números más grandes queint
pueden.Para depurar tales casos, es bueno reducir el número de iteraciones en el ciclo. Use esto en lugar de su
while(true)
:Luego puede ver que comienza con 2 y duplica el valor hasta que causa un desbordamiento.
fuente
Usaré un número de 8 bits como ilustración porque se puede detallar completamente en un espacio corto. Los números hexadecimales comienzan con 0x, mientras que los números binarios comienzan con 0b.
El valor máximo para un entero sin signo de 8 bits es 255 (0xFF o 0b11111111). Si agrega 1, normalmente esperaría obtener: 256 (0x100 o 0b100000000). Pero como son demasiados bits (9), está por encima del máximo, por lo que la primera parte simplemente se elimina, dejándolo con 0 de manera efectiva (0x (1) 00 o 0b (1) 00000000, pero con el 1 eliminado).
Entonces, cuando su programa se ejecuta, obtiene:
fuente
El literal decimal más grande de tipo
int
es 2147483648 (= 2 31 ). Todos los literales decimales de 0 a 2147483647 pueden aparecer en cualquier lugar donde pueda aparecer un literal int, pero el literal 2147483648 puede aparecer solo como el operando del operador de negación unario -.Si una suma entera se desborda, entonces el resultado son los bits de orden inferior de la suma matemática representados en algún formato de complemento a dos suficientemente grande. Si se produce un desbordamiento, el signo del resultado no es el mismo que el signo de la suma matemática de los dos valores de operando.
fuente