java - sirve - ¿Por qué escribir un número en notación científica hace una diferencia en este código?
para que sirve la notacion cientifica (3)
@rgettman ya ha dado detalles sobre la gimnasia de ronda que se lleva a cabo cuando se utiliza un double
lugar de un long
. Pero hay más.
Cuando agregas repetidamente un número grande a un long
, eventualmente obtendrás un resultado negativo. Por ejemplo, Long.MAX_VALUE + 1L = Long.MIN_VALUE
. Cuando eso suceda, simplemente repetirás el proceso indefinidamente.
Así que si cambiaste tu código a:
while (cumSeconds >= 0L) {
// 31557600000 is the number of milliseconds in a year
cumSeconds += 31557600000L;
cumSeconds
donde las cosas se vuelven negativas porque cumSeconds
rodó.
Estoy tratando de escribir un código para determinar cuándo la cantidad de milisegundos desde principios de 1970 excederá la capacidad de un largo. El siguiente código parece hacer el trabajo:
public class Y2K {
public static void main(String[] args) {
int year = 1970;
long cumSeconds = 0;
while (cumSeconds < Long.MAX_VALUE) {
// 31557600000 is the number of milliseconds in a year
cumSeconds += 3.15576E+10;
year++;
}
System.out.println(year);
}
}
Este código se ejecuta en segundos e imprime 292272992. Si en lugar de usar la notación científica escribo cumSeconds como 31558000000L
, el programa parece tardar "para siempre" en ejecutarse (solo presiono pausar después de 10 minutos aproximadamente). También tenga en cuenta que escribir cumSeconds en notación científica no requiere especificar que el número sea long
con L o l al final.
La observación clave es que cumSeconds < Long.MAX_VALUE
donde cumSeconds
es un valor long
solo puede ser falso si cumSeconds
es exactamente Long.MAX_VALUE
.
Si realiza el cálculo con números largos, se tarda bastante tiempo en alcanzar este valor exactamente (si es que se alcanza) porque la aritmética larga se ajusta cuando abandona el rango de números.
Al hacer la aritmética con números dobles se obtendrá el valor máximo cuando el valor doble sea lo suficientemente grande.
La razón por la que hace una diferencia es porque la notación científica número 3.1558E+10
es un literal double
, mientras que el literal 31558000000L
es, por supuesto, un literal long
.
Esto hace toda la diferencia en el operador +=
.
Una expresión de asignación compuesta de la forma E1 op = E2 es equivalente a E1 = (T) ((E1) op (E2)), donde T es el tipo de E1, excepto que E1 se evalúa solo una vez.
Básicamente, largo + = largo produce un largo, pero largo + = doble también produce un largo.
Al agregar un double
, el valor inicial de cumSeconds
se amplía a un double
y luego se produce la adición. El resultado experimenta una conversión primitiva de estrechamiento de nuevo a long
.
Una conversión de reducción de un número de punto flotante a un tipo T integral toma dos pasos:
- En el primer paso, el número de punto flotante se convierte a largo, si T es largo
(recorte)
De lo contrario, uno de los dos casos siguientes debe ser verdadero:
El valor debe ser demasiado pequeño (un valor negativo de magnitud grande o infinito negativo), y el resultado del primer paso es el valor representable más pequeño del tipo int o long.
El valor debe ser demasiado grande (un valor positivo de gran magnitud o infinito positivo), y el resultado del primer paso es el mayor valor representable del tipo int o long .
(negrita énfasis mío)
El resultado eventualmente es demasiado grande para ser representado en un long
, por lo que el resultado se Long.MAX_VALUE
a Long.MAX_VALUE
, y el bucle while termina.
Sin embargo, cuando usa un literal long
, continuamente agrega un valor par a un valor par, que eventualmente se desbordará. Esto no establece el valor en Long.MAX_VALUE
, que es impar, por lo que el bucle es infinito.
Pero en lugar de confiar en una adición que finalmente Long.MAX_VALUE
, con Java 1.8+ puede probar explícitamente el desbordamiento con Math.addExact
.
Devuelve la suma de sus argumentos, lanzando una excepción si el resultado se desborda un largo.
Tiros
ArithmeticException
- si el resultado se desborda un largo