Вопрос-ответ

Why does Java implicitly (without cast) convert a `long` to a `float`?

Почему Java неявно (без приведения) преобразует `long` в `float`?

Каждый раз, когда я думаю, что понимаю, что такое приведение и преобразования, я обнаруживаю очередное странное поведение.

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

Учитывая, что a long имеет большую разрядность, чем a float, я ожидаю, что для компиляции потребуется явное приведение. И неудивительно, что мы видим, что потеряли точность результата.

Почему приведение здесь не требуется?

Переведено автоматически
Ответ 1

В спецификации языка Java, глава 5: Преобразование и продвижение рассматривается эта проблема:


5.1.2 Расширяющее преобразование примитивов


Следующие 19 конкретных преобразований для примитивных типов называются расширяющимися примитивными преобразованиями:



  • байт в short, int, long, float или double

  • short в int, long, float или double

  • char в int, long, float или double

  • int в long, float или double

  • long в float или double

  • float в double


Расширяющиеся преобразования примитивов не теряют информацию об общей величине числового значения.


...


Преобразование int или long значения в float или long значения в double может привести к потере точности, то есть в результате могут быть потеряны некоторые из младших значащих битов значения. В этом случае результирующее значение с плавающей запятой будет корректно округленной версией целого значения


Другими словами, JLS проводит различие между потерей величины и потерей точности.

int например, для byte это (потенциальная) потеря величины, потому что вы не можете сохранить 500 в byte.

long в float - это потенциальная потеря точности, но не величины, потому что диапазон значений для float больше, чем для longs .

Итак, правило таково:


  • Потеря величины: требуется явное приведение;

  • Потеря точности: приведение не требуется.

Тонко? Конечно. Но я надеюсь, что это прояснит ситуацию.

Ответ 2

Тот же вопрос можно задать для long to double - оба преобразования могут привести к потере информации.

Раздел 5.1.2 спецификации языка Java гласит:


Расширяющиеся преобразования примитивов не теряют информацию об общей величине числового значения. Действительно, преобразования, расширяющиеся из целочисленного типа в другой целочисленный тип, вообще не теряют никакой информации; числовое значение сохраняется в точности. Преобразования, расширяющиеся от float до double в выражениях strictfp, также в точности сохраняют числовое значение; однако такие преобразования, которые не являются strictfp, могут потерять информацию об общей величине преобразованного значения.


Преобразование int или long значения в float или long значения в double может привести к потере точности, то есть в результате могут быть потеряны некоторые из младших значащих битов значения. В этом случае результирующее значение с плавающей запятой будет корректно округленной версией целого значения с использованием режима округления до ближайшего значения IEEE 754 (§4.2.4).


Другими словами, даже если вы можете потерять информацию, вы знаете, что значение все равно будет находиться в общем диапазоне целевого типа.

Безусловно, можно было бы сделать выбор, требующий, чтобы все неявные преобразования вообще не приводили к потере информации - так что int и long to float были бы явными, а long to double были бы явными. (int to double - это нормально; a double обладает достаточной точностью для точного представления всех int значений.)

В некоторых случаях это было бы полезно, в некоторых случаях нет. Языковой дизайн - это компромисс; вы не можете победить их всех. Я не уверен, какое решение я бы принял...

Ответ 3

Хотя вы правы в том, что long использует больше битов внутри, чем float , язык Java работает по расширяющемуся пути:

byte -> short -> int -> long -> float -> double

Для преобразования слева направо (расширяющее преобразование) приведение не требуется (именно поэтому допускается преобразование long в float). Для преобразования справа налево (сужающее преобразование) необходимо явное приведение.

Ответ 4

Где-то я это слышал. Float может храниться в экспоненциальной форме, как мы ее записываем. '23500000000' хранится как '2.35e10' . Таким образом, у float есть место, чтобы занимать диапазон значений long. Сохранение в экспоненциальной форме также является причиной потери точности.

java