Я читал о значениях NaN с плавающей запятой в спецификации языка Java (мне скучно). 32-разрядный float
имеет этот бит-формат:
seee eeee emmm mmmm mmmm mmmm mmmm mmmm
s
- знаковый бит, e
- биты экспоненты, а m
- бит мантиссы. Значение NaN кодируется как показатель всех 1s, а биты мантиссы - не все 0 (что равно +/- бесконечности). Это означает, что существует множество различных возможных значений NaN (имеющих разные значения бит s
и m
).
В этом случае JLS §4.2.3 говорится:
IEEE 754 допускает множество различных значений NaN для каждого из своих одно- и двухплановых форматов с плавающей запятой. Хотя каждая аппаратная архитектура возвращает конкретный бит-шаблон для NaN при генерации нового NaN, программист может также создавать NaN с разными битовыми шаблонами для кодирования, например ретроспективной диагностической информации.
Текст в JLS, по-видимому, подразумевает, что результат, например, 0.0/0.0
, имеет аппаратно-зависимый битовый шаблон и в зависимости от того, было ли это выражение вычислено как константа времени компиляции, аппаратное обеспечение зависит от него возможно, это аппаратное обеспечение, скомпилированное программой Java, или аппаратное обеспечение, на котором была запущена программа. Это все кажется очень шелушащимся, если это правда.
Я проверил следующий тест:
System.out.println(Integer.toHexString(Float.floatToRawIntBits(0.0f/0.0f)));
System.out.println(Integer.toHexString(Float.floatToRawIntBits(Float.NaN)));
System.out.println(Long.toHexString(Double.doubleToRawLongBits(0.0d/0.0d)));
System.out.println(Long.toHexString(Double.doubleToRawLongBits(Double.NaN)));
Выход на моей машине:
7fc00000
7fc00000
7ff8000000000000
7ff8000000000000
Результат не показывает ничего из ожидаемого. Биты экспоненты все 1. Верхний бит мантиссы также равен 1, что для NaN, по-видимому, указывает на "спокойное NaN", а не на "сигнализацию NaN" (https://en.wikipedia.org/wiki/NaN#Floating_point). Битовый знак и остальные биты мантиссы равны 0. Вывод также показывает, что не было никакой разницы между NaN, сгенерированными на моей машине, и постоянными NaN из классов Float и Double.
Мой вопрос: - это результат, гарантированный на Java, независимо от процессора компилятора или VM, или все это действительно непредсказуемо? JLS таинственна в этом.
Если этот результат гарантирован для 0.0/0.0
, существуют ли какие-либо арифметические способы создания NaN, у которых есть другие (возможно, зависящие от оборудования?) битовые шаблоны? (Я знаю, что intBitsToFloat
/longBitsToDouble
может кодировать другие NaN, но я хотел бы знать, могут ли другие значения возникать из обычной арифметики.)
Следующий пункт: я заметил, что Float.NaN и Double.NaN указывают их точную битовую структуру, но в источнике (Float, Double), они генерируются 0.0/0.0
. Если результат этого разделения действительно зависит от аппаратного обеспечения компилятора, кажется, что существует недостаток в спецификации или реализации.