¿Por qué Java implícitamente (sin conversión) convierte un `long` en un` float`?

Cada vez que creo que entiendo sobre conversión y conversión, encuentro otro comportamiento extraño.

long l = 123456789L; float f = l; System.out.println(f); // outputs 1.23456792E8 

Dado que un long tiene una profundidad de bit mayor que un float , esperaría que se requiera un molde explícito para que se pueda comstackr. Y no es sorprendente que veamos que hemos perdido precisión en el resultado.

¿Por qué no se requiere un yeso aquí?

Se podría pedir la misma pregunta de long para double : ambas conversiones pueden perder información.

La Sección 5.1.2 de la Especificación de Lenguaje Java dice:

Las conversiones primitivas ampliadas no pierden información sobre la magnitud general de un valor numérico. De hecho, las conversiones que se amplían de un tipo integral a otro tipo integral no pierden ninguna información; el valor numérico se conserva exactamente. Las conversiones que se amplían de float a double en las expresiones strictfp también conservan el valor numérico exactamente; sin embargo, dichas conversiones que no son strictfp pueden perder información sobre la magnitud general del valor convertido.

La conversión de un valor int o un valor largo en float, o de un valor largo en double, puede dar como resultado la pérdida de precisión, es decir, el resultado puede perder algunos de los bits menos significativos del valor. En este caso, el valor del punto flotante resultante será una versión redondeada correctamente del valor entero, utilizando el modo IEEE 754 redondeado a más cercano (§4.2.4).

En otras palabras, aunque pueda perder información, sabe que el valor seguirá estando en el rango general del tipo de destino.

La elección ciertamente podría haberse hecho para exigir que todas las conversiones implícitas no pierdan ninguna información, por lo que int y long para float habría sido explícito y long para double habría sido explícito. ( int al double está bien, un double tiene la precisión suficiente para representar con precisión todos los valores int ).

En algunos casos eso hubiera sido útil, en algunos casos no. El diseño del lenguaje se trata de compromiso; no puedes ganarlos a todos. No estoy seguro de qué decisión habría tomado …

La Especificación del lenguaje Java, Capítulo 5: Conversión y promoción aborda este problema:

5.1.2 Ampliación de conversión primitiva

Las siguientes 19 conversiones específicas sobre tipos primitivos se llaman conversiones primitivas de ensanchamiento:

  • byte a corto, int, largo, flotante o doble
  • corto a int, largo, flotante o doble
  • char a int, long, float o double
  • int a long, float o double
  • largo para flotar o doble
  • flotar para duplicar

Las conversiones primitivas ampliadas no pierden información sobre la magnitud general de un valor numérico.

La conversión de un valor int o un valor largo en float, o de un valor largo en double, puede dar como resultado la pérdida de precisión, es decir, el resultado puede perder algunos de los bits menos significativos del valor. En este caso, el valor del punto flotante resultante será una versión redondeada correctamente del valor entero

Para decirlo de otra manera, el JLS distingue entre una pérdida de magnitud y una pérdida de precisión .

int to byte por ejemplo, es una pérdida (potencial) de magnitud porque no puede almacenar 500 en un byte .

long para float es una pérdida potencial de precisión, pero no de magnitud, porque el rango de valores para los flotadores es mayor que el de los largos.

Entonces la regla es:

  • Pérdida de magnitud: requiere conversión explícita;
  • Pérdida de precisión: no se requiere conversión.

¿Sutil? Por supuesto. Pero espero que eso lo aclare.

Aunque tienes razón en que una larga usa más bits internamente que una coma flotante, el lenguaje Java funciona en una ruta de ensanchamiento:

byte -> corto -> int -> largo -> flotante -> doble

Para convertir de izquierda a derecha (una conversión de ampliación), no es necesario el envío (por lo que está permitido el tiempo de flotación). Para convertir de derecha a izquierda (una conversión de estrechamiento) es necesario un lanzamiento explícito.

En algún lugar oí esto. Float puede almacenar en forma exponencial como lo escribimos. ‘23500000000’ se almacena como ‘2.35e10’. Así que, el flotador tiene espacio para ocupar el rango de valores de largo. El almacenamiento en forma exponencial es también el motivo de la pérdida de precisión.