Приведение типа double к другому числовому типу

что-то меня озадачивает, и я не нашел много информации о спецификациях виртуальной машины. Это немного неясно, и было бы неплохо, если бы кто-нибудь мог мне объяснить.

Эти несколько строк кода.....

double myTest = Double.MAX_VALUE;

System.out.println("1. float: " + (float)myTest);
System.out.println("2. int: " + (int)myTest);
System.out.println("3. short: " + (short)myTest);
System.out.println("4. byte: " + (byte)myTest);

..... произвести этот вывод:

  1. поплавок: бесконечность
  2. инт: 2147483647
  3. коротко: -1
  4. байт: -1

byte, short и int — это 8, 16, 32 бита с дополнением до двух. float и double — это 32- и 64-разрядные версии IEEE 754 (см. здесь).

Насколько я понимаю, максимальное значение double подразумевает, что все биты мантиссы (52 бита) переключаются на 1. Поэтому не (очень) удивительно, что приведение к короткому или байтовому возвращает -1, т.е. все биты переключаются до 1. Кажется, что приведение сохраняет «хвост» double, чтобы он соответствовал 8-битному byte или 16-битному short.

Что меня удивляет, так это актерский состав int и, в меньшей степени, актерский состав float. Как можно получить «2. int: 2147483647», который равен 0x7FFFFFFFF, максимальное значение пока короткое, а байты 3 и 4 равны -1?

Актерский состав float тоже странный. Если бы 32 бита в «хвосте» myTest были сохранены, то не должен ли он генерировать NaN ?


person Jerome    schedule 07.05.2012    source источник


Ответы (1)


JLS разъясняет правила в разделе 5.1.3 Сужающее примитивное преобразование. Правила зависят от типа цели.

float:

Сужающее примитивное преобразование из double в float регулируется правилами округления IEEE 754 (§4.2.4). Это преобразование может привести к потере точности, но также и к потере диапазона, что приведет к нулю с плавающей запятой из ненулевого двойного числа и к бесконечности с плавающей запятой из конечного двойного числа. Двойной NaN преобразуется в число NaN с плавающей запятой, а двойная бесконечность преобразуется в бесконечность с плавающей запятой с тем же знаком.

int и long:

должен быть верным один из следующих двух случаев:

  • ...
  • Значение должно быть слишком большим (положительное значение большой величины или положительная бесконечность), а результатом первого шага является наибольшее представимое значение типа int или long.

byte, char и short:

Если целевой тип byte, char или short, преобразование выполняется в два этапа. Во-первых, double преобразуется в long, как описано выше. Затем long преобразуется в окончательный тип следующим образом:

Сужающее преобразование целого числа со знаком в целочисленный тип T просто отбрасывает все, кроме n битов младшего порядка, где n — количество битов, используемых для представления типа T. В дополнение к возможной потере информации о величине числового значения , это может привести к тому, что знак результирующего значения будет отличаться от знака входного значения.

person NPE    schedule 07.05.2012
comment
Спасибо это очень интересно. Таким образом, как только двойное число приводится к типу int (с наибольшим представимым значением в моем примере), оно приводится к байту или короткому значению с другой философией (отбрасывает все биты, кроме n младших). - person Jerome; 07.05.2012
comment
@Jerome: В значительной степени (за исключением того, что я думаю, что промежуточный тип - long, но это не влияет на результат). - person NPE; 07.05.2012
comment
Стоит отметить, что хотя приведение double к float может привести к тому, что некоторые различные значения станут неразличимыми, это незначительная проблема по сравнению с тем фактом, что приведение float к double может привести к тому, что вещи, которые следует считать неразличимыми, вместо этого будут отсортированы неправильно. Например, учитывая float f=16777217; double d=16777216.0000001;, что больше — f или d? Как насчет float ff=1E38f*10f; double dd=1e300;? Считать ff и dd неразличимыми было бы не очень хорошо, но говорить о ff>dd неправильно на сотни порядков. - person supercat; 04.09.2013