java - El desbordamiento se produce con la multiplicación.
multiplication integer-overflow (6)
En este caso -
long m = 24 * 60 * 60 * 1000 * 1000;
El derecho de la asignación se evalúa primero. A la derecha no hay datos de tipo long
. Todos son int
. Así que la JVM
intenta ajustar el resultado en un int
luego se produjo el desbordamiento.
Y en el segundo caso ...
long m2 = 24L * 60 * 60 * 1000 * 1000;
long m3 = 24 * 60 * 60 * 1000 * 1000L;
Aquí un operando de la multiplicación es long
. Por lo que otros se les pide que se long
automáticamente. El resultado es tratar de encajar a un long
. Finalmente la asignación se realiza con m2
y m3
.
Y sí, la asociatividad de la multiplicación de izquierda a derecha significa que el operando izquierdo se toma primero. Y en base a este hecho, creo que en este escenario deberíamos usar:
long m2 = 24L * 60 * 60 * 1000 * 1000;
esta declaración, ya que en esta declaración la promoción a lugares tomados long
antes, lo que reduce el riesgo de desbordamiento.
long m = 24 * 60 * 60 * 1000 * 1000;
El código anterior crea un desbordamiento y no imprime el resultado correcto.
long m2 = 24L * 60 * 60 * 1000 * 1000;
long m3 = 24 * 60 * 60 * 1000 * 1000L;
Las 2 líneas anteriores imprimen el resultado correcto.
Mis preguntas son-
- ¿Importa al compilador que uso,
m2
om3
? - ¿Cómo empieza Java a multiplicarse? De izquierda a derecha o de derecha a izquierda? ¿Se calculan primero 24 * 60 o 1000 * 1000?
Esto se debe a que cuando usamos un largo tiempo como un operando y el otro, todos los int
tipo int
se nos solicitan a long
.
La expresión en java se evalúa de izquierda a derecha.
Multiplicando trabajos de izquierda a derecha, e int * int
produce int
. Asi que
24 * 60 * 60 * 1000 * 1000
es igual que
(((24 * 60)* 60) * 1000) * 1000
lo que nos da
(((1440)* 60) * 1000) * 1000
(( 86400 ) * 1000) * 1000
( 86400000 ) * 1000
y, finalmente, debido al desbordamiento de enteros (ya que 86400000000
es demasiado grande para un entero cuyo valor máximo es 2147483647
) el resultado será
500654080
Puede eliminar el desbordamiento de enteros usando long
como uno de los argumentos ( int * long
y long * int
produce long
).
En este caso, puede hacerlo al inicio como lo hizo en el caso de m2
: 24L * 60
que producirá 1440L
long
que, de nuevo, se multiplicarán por int 60
produciendo nuevos long
, y así sucesivamente, produciendo solo valores long
.
m3
caso m3
funciona aquí porque está multiplicando 86400000
por 1000L
que significa que está evitando el desbordamiento de enteros ya que el resultado será long
.
Vamos a multiplicar más números, esta línea se desbordará incluso si hay un 1000L
:
long m3 = 24 * 60 * 60 * 1000 * 1000 * 1000 * 1000L;
Si bien esto dará resultado correcto:
long m3 = 24L * 60 * 60 * 1000 * 1000 * 1000 * 1000;
Así que estamos seguros de que Java comienza a multiplicarse de izquierda a derecha y debemos comenzar con Long
desde la izquierda para evitar el desbordamiento.
Yo usaría la línea m2
lugar de la línea m3
.
Java evalúa el operador de multiplicación *
de izquierda a derecha , por lo que primero se evalúa 24 * 60
.
Da la casualidad de que 24 * 60 * 60 * 1000
(un 1000
) no se desborda, por lo que para el momento en que se multiplique por 1000L
(el segundo 1000
), el producto se promociona long
antes de multiplicarse, por lo que el desbordamiento no Tendrá lugar.
Pero como mencionó en sus comentarios, más factores pueden causar un desbordamiento en el tipo de datos int
antes de multiplicar el último número long
, dando una respuesta incorrecta. Es mejor usar un literal long
para el primer número (el más a la izquierda) como en m2
para evitar el desbordamiento desde el principio. Alternativamente, puede convertir el primer literal como un long
, por ejemplo, (long) 24 * 60 * ...
Como las expresiones se evalúan de izquierda a derecha , preferiría su primera solución ( m2 = ...
).
Razonamiento: veamos un ejemplo ligeramente diferente.
long g = Integer.MAX_VALUE * 2 * 2L;
Esta expresión se evaluará en -4
ya que solo la última multiplicación convierte la primera expresión en long
(que es -2
en este momento, porque ambos operandos son int
). Si tú escribes
long g = Integer.MAX_VALUE * 2L * 2;
en cambio, g
mantendrá el valor esperado de 8589934588
ya que la primera multiplicación produce un resultado de tipo long
.