Почему приведение Double.NaN к int не вызывает исключение в Java? - PullRequest
18 голосов
/ 04 мая 2011

Итак, я знаю, что IEEE 754 определяет некоторые специальные значения с плавающей запятой для значений, которые не являются действительными числами. В Java приведение этих значений к примитиву int действительно , а не вызывает исключение, как я и ожидал. Вместо этого у нас есть следующее:

int n;
n = (int)Double.NaN; // n == 0
n = (int)Double.POSITIVE_INFINITY; // n == Integer.MAX_VALUE
n = (int)Double.NEGATIVE_INFINITY; // n == Integer.MIN_VALUE

Каково обоснование для не исключения в этих случаях? Является ли это стандартом IEEE, или это был просто выбор разработчиков Java? Есть ли плохие последствия, о которых я не знаю, если бы исключения могли быть с такими бросками?

Ответы [ 5 ]

11 голосов
/ 04 мая 2011

Каково обоснование для того, чтобы не выбрасывать исключения в этих случаях?

Я полагаю, что причины включают в себя:

  • Это крайние случаи, и они редко встречаются в приложениях, которые делают подобные вещи.

  • Поведение не является «совершенно неожиданным».

  • Когда приложение преобразуется из типа double в int, ожидается значительная потеря информации.Приложение либо будет игнорировать эту возможность, либо перед исполнением будут проверяться средства защиты от него ... которые также могут проверять эти случаи.

  • Никаких других двойных / плавающих чиселоперации приводят к исключениям, и (IMO) было бы немного шизофренично делать это в этом случае.

  • Возможно, может произойти падение производительности ... на некоторых аппаратных платформах (в настоящее времяили будущее).

Является ли это стандартом IEEE или это был просто выбор разработчиков Java?

Последнее, я думаю.

Есть ли плохие последствия, о которых я не знаю, если бы исключения могли быть с такими бросками?

Ничего, кроме очевидных ...

(Но это не очень важно. Спецификации JLS и JVM говорят то, что говорят, и их изменение может привести к поломке существующего кода. И это не простоJava-код, о котором мы сейчас говорим ...)


Я немного покопался.Многие инструкции x86, которые можно использовать для преобразования из двойных в целые, похоже, генерируют аппаратные прерывания ... если не маскированы.Мне неясно, легче или сложнее реализовать указанное поведение Java, чем альтернатива, предложенная ОП.

8 голосов
/ 04 мая 2011

Что является основанием для того, чтобы не бросать исключения в этих случаях? Это Стандарт IEEE, или это был просто Выбор дизайнерами Java?

Стандарт IEEE 754-1985 на страницах 20 и 21 в разделах 2.2.1 NAN и 2.2.2 Infinity четко объясняет причины, по которым значения NAN и Infinity требуются стандартом. Поэтому это не вещь Java.

Спецификация виртуальной машины Java в разделе 3.8.1 Арифметика с плавающей точкой и IEEE 754 утверждают, что при выполнении преобразования в целочисленные типы JVM будет применять округление до нуля, которое объясняет результаты, которые вы видите.

В стандарте упоминается функция с именем «обработчик ловушек», которая может использоваться для определения того, когда происходит переполнение или NAN, но в спецификации виртуальной машины Java четко указано, что она не реализована для Java. В разделе 3.8.1 сказано:

Операции с плавающей точкой Виртуальную машину Java не выбрасывают исключения, ловушка или другой сигнал IEEE 754 исключительные условия недопустимая операция, деление на ноль, переполнение, недостаточное или неточное. Виртуальная машина Java не имеет сигнализации Значение NaN.

Итак, поведение не является неопределенным независимо от последствий.

Есть ли плохие последствия, что я не зная, будут ли исключения можно с такими слепками?

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

EDIT

Я читал последний обзор технического обслуживания Спецификации виртуальной машины Java , опубликованной недавно JCP в рамках своей работы над JSR 924 и в разделе 2.11.14 под названием istructions для преобразования типов содержит дополнительную информацию, которая может помочь вам в поиске ответов, еще не то, что вы ищете, но я считаю, что это немного помогает. Там написано:

В сужающемся числовом преобразовании значение с плавающей точкой в ​​целое тип T, где T либо int, либо long, значение с плавающей точкой преобразуется следующим образом:

  • Если значение с плавающей запятой равно NaN, результатом преобразования будет
    int или long 0.
  • В противном случае, если значение с плавающей запятой не является бесконечностью,
    значение с плавающей точкой округляется до
    целое значение V с использованием IEEE 754
    Округление до нулевого режима.

Есть два случая:

  • Если T длинная и это целочисленное значение может быть представлено как длинная, тогда
    В результате получается длинное значение V.
  • Если T имеет тип int и это целочисленное значение может быть представлено как int, то результатом является int значение V.

В противном случае:

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

Сужающееся числовое преобразование из double to float ведет себя в соответствии с IEEE 754. Результат правильно округляется с использованием IEEE 754 до Ближайший режим. Значение слишком маленькое, чтобы быть в виде числа с плавающей точкой преобразуется в положительный или отрицательный ноль типа плавать; слишком большое значение в виде числа с плавающей точкой преобразуется в положительная или отрицательная бесконечность. двойной NaN всегда преобразуется в поплавок NaN.

DНесмотря на то, что может произойти переполнение, потеря или потеря точности, сужение преобразований среди числовых типов никогда не заставляет виртуальную машину Java генерировать исключение времени выполнения (не путать с исключением с плавающей точкой IEEE 754).

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

EDIT

Соответствующий стандарт IEEE в разделе 2.3.2 Режимы округления Состояния:

По умолчанию округление означает округление до ближайшего.Стандарт требует наличия трех других режимов округления;а именно, округление до 0, округление до + Infinity и округление до –Infinity.

При использовании с операцией преобразования в целое число округление до –Infinity приводит к тому, что преобразование становится функцией пола, тогда как округление до + Infinityявляется потолком.

Округление режима влияет на переполнение, поскольку при действии округления в направлении O или округления в направлении -Infinite переполнение положительной величины приводит к тому, что результатом по умолчанию будет наибольшее представимое число, а не + Infinity.

Аналогично, переполнения с отрицательной величиной приведут к наибольшему отрицательному числу, когда действует округление к + бесконечности или округление к О.

Затем они приводят пример того, почему это полезнов интервальной арифметике.Не уверен, опять же, что это ответ, который вы ищете, но он может обогатить ваш поиск.

3 голосов
/ 29 августа 2016

Существует презентация ACM 1998 года, которая все еще кажется удивительно актуальной и дает некоторый свет: https://people.eecs.berkeley.edu/~wkahan/JAVAhurt.pdf.

Конкретнее, относительно удивительного отсутствия исключений при приведении NaN и бесконечностей: см. Стр. 3, пункт 3: «Бесконечность и NaNs раскрываются без защиты ловушек с плавающей точкой и флагов, предусмотренных стандартами IEEE 754/854, несмотря на претензии Java на надежность».

Презентация на самом деле не отвечает «почему», но объясняет последствия проблемных проектных решений в реализации языка с плавающей запятой на языке Java и помещает их в контекст стандартов IEEE и даже другихреализации.

1 голос
/ 04 мая 2011

На самом деле, я думаю, что во время некоторых кастований выполняются битовые операции (возможно, для проблем с перфорированием?), Чтобы вы могли иметь некоторые неожиданные поведения.Посмотрите, что происходит, когда вы используете операторы >> и <<. </p>

Например:

public static void main(String[] args) {
    short test1 = (short)Integer.MAX_VALUE;
    System.out.println(test1);
    short test2 = (short)Integer.MAX_VALUE-1;
    System.out.println(test2);
    short test3 = (short)Integer.MAX_VALUE-2;
    System.out.println(test3);
    short test4 = (short)Double.MAX_VALUE-3;
    System.out.println(test4);
}

выведет:

-1
-2
-3
-4
1 голос
/ 04 мая 2011

Это в JLS, см .: JavaRanch post http://www.coderanch.com/t/239753/java-programmer-SCJP/certification/cast-double-int Однако предупреждение будет хорошим.

...