settitle - ¿Es este comportamiento de doble análisis de Java de acuerdo con las especificaciones?
para que sirve settitle en java (3)
Creo que es un caso extremo, pero también un error. Un ejemplo más simple es
String text = "0.000000000000000001e326";
System.out.println(Double.parseDouble(text));
System.out.println(new BigDecimal(text).doubleValue());
que imprime en Java 7 update 25 y Java 8 update 5
Infinity
1.0E308
El análisis BigDecimal y la conversión a doble muestra que este número es representable.
El método java.lang.Double.parseValue
maneja representaciones de dobles de forma extraña de una manera inconsistente.
Si escribe un número muy grande, tan grande que está fuera del rango del double
, pero luego agrega un exponente negativo grande para volver a ponerlo en rango, termina en el rango (ilustrado aquí en el REPL de Scala):
scala>
java.lang.Double.parseDouble("10000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000001e-400")
res25: Double = 1.0E-21
Por otro lado, si escribe un número muy pequeño, tan pequeño que está fuera del rango del double
, pero luego usa un gran exponente positivo para volverlo a poner en rango, solo funciona si el exponente en sí no es demasiado grande. :
scala>
java.lang.Double.parseDouble("0.000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000001e400")
res26: Double = Infinity
scala>
java.lang.Double.parseDouble("0.000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000001e200")
res27: Double = 1.0E-179
¿Es esto simplemente un error, o hay una especificación en alguna parte que permite este comportamiento, o todas estas son permitidas por la especificación para fallar y uno simplemente debería agradecer las bendiciones de uno cuando uno obtiene el resultado correcto? (Si es un error, ¿ha sido reparado?)
(Aparte: estaba escribiendo código String-to-double personalizado e iba a diferir a la implementación predeterminada de Java para casos complicados, pero este caso de prueba falló).
Es casi seguro que no está en la especificación. La sección correspondiente sobre Literales de punto flotante en el JLS solo especifica los valores de los literales de coma flotante. Pero no habla de representaciones válidas de ellos.
Por supuesto, tiene que haber límites. Nadie esperaría que una cadena como
String s = "0.00... (3 billion zeros) ...001e3000000000";
para ser analizado a 1.0
. Pero, obviamente, los límites son mucho más bajos aquí.
Este ejemplo muestra el límite:
public class DoubleTest
{
public static void main(String[] args)
{
runTest(300, 324);
runTest(300, 325);
runTest(300, 326);
}
private static void runTest(int negativeExponent, int exponent)
{
String s = prefix(negativeExponent)+"1e"+exponent+"D";
double d = Double.parseDouble(s);
System.out.println(
"For 1e-"+negativeExponent+" * 1e"+exponent+" result is "+d);
}
private static String prefix(int negativeExponent)
{
StringBuilder sb = new StringBuilder("0.");
for (int i=0; i<negativeExponent; i++)
{
sb.append("0");
}
return sb.toString();
}
}
Imprime
Para 1e-300 * 1e324 el resultado es 9.999999999999999E22
Para 1e-300 * 1e325 el resultado es 1.0E24
Para 1e-300 * 1e326 el resultado es infinito
De hecho, está relacionado principalmente con el exponente que se usa. La parte relevante que causa este rescate se encuentra en FloatingDecimal.java, línea 1996 .
Es un error, pero mirando la implementación, Oracle podría decir que está diseñado .
La forma en que se implementa es que los kDigits
principales se convierten a int largo. Como tal, en el primer ejemplo, el exponente calculado está dentro del rango Double
y felizmente devuelve el resultado si está dentro del rango.
Para el segundo caso, supondría que el exponente es mayor que el máximo exponente decimal y devuelve infinito.
Para el tercer caso, llegaría aquí y arrojaría el resultado esperado.
Aunque los enlaces de arriba apuntan a la fuente de OpenJDK 6, es poco probable que hayan tocado la fuente en cuestión para JDK 7 y 8.
Parsing dobles ha sido tradicionalmente fun . No hay sorpresas por las rarezas en este caso.