logo
Методичка Java

Стандарт ieee 754 представления чисел в формате с плавающей точкой*

*Этот параграф является необязательным и приводится в справочных целях

В каком виде на самом деле хранятся числа в формате с плавающей точкой? Ответ даёт стандарт IEEE 754 (другой вариант названия IEC 60559:1989), разработанный для электронных счётных устройств. В этом стандарте предусмотрены три типа чисел в формате с плавающей точкой, с которыми могут работать процессоры: real*4, real*8 и real*10. Эти числа занимают 4, 8 и 10 байт, соответственно. В Java типу real*4 соответствует float, а типу real*8 соответствует double. Тип real*10 из распространённых языков программирования используется только в диалектах языка PASCAL, в Java он не применяется.

Число r представляется в виде произведения знака s, мантиссы m и экспоненты 2p-d :

r=s*m*2p-d.

Число p называется порядком. Оно может меняться для разных чисел. Значение d, называемое сдвигом порядка, постоянное для всех чисел заданного типа. Оно примерно равно половине максимального числа pmax, которое можно закодировать битами порядка. Точнее, d= (pmax+1)/2-1.

Для чисел real*4: pmax = 255, d= 127.

Для чисел real*8: pmax = 2047, d= 1023.

Для чисел real*10: pmax =32767, d=16383.

Число называется нормализованным в случае, когда мантисса лежит в пределах 1≤m<2. В этом случае первый бит числа всегда равен единице. Максимальное значение мантиссы достигается в случае, когда все её биты равны 1. Оно меньше 2 на единицу младшего разряда мантиссы, то есть с практически важной точностью может считаться равным 2.

Согласно стандарту IEEE 754 все числа формата с плавающей точкой при значениях порядка в диапазоне от 1 до pmax-1 хранятся в нормализованном виде. Такое представление чисел будем называть базовым. Когда порядок равен 0, применяется несколько другой формат хранения чисел. Будем называть его особым. Порядок pmax резервируется для кодировки нечисловых значений, соответствующее представление будем называть нечисловым. Об особом и нечисловом представлениях чисел будет сказано чуть позже.

Размещение чисел в ячейках памяти такое:

Тип

Байт1

Байт2

Байт3

Байт4

Байт8

Байт9

Байт10

real*4

sppp pppp

pmmm mmmm

mmmm mmmm

mmmm mmmm

real*8

sppp pppp

pppp mmmm

mmmm mmmm

mmmm mmmm

mmmm mmmm

real*10

sppp pppp

pppp pppp

1mmm mmmm

mmmm mmmm

mmmm mmmm

mmmm mmmm

mmmm mmmm

Буква s обозначает знаковый бит; p – биты двоичного представления порядка, m – биты двоичного представления мантиссы. Если знаковый бит равен нулю, число положительное, если равен единице – отрицательное. В числах real*4 (float) и real*8 (double) при базовом представлении ведущая единица мантиссы подразумевается, но не хранится, поэтому реально можно считать, что у них под мантиссу отведено не 23 и 52 бита, которые реально хранятся, а 24 и 53 бита. В числах real*10 ведущая единица мантиссы реально хранятся, и мантисса занимает 64 бит. Под порядок в числах real*4 отведено 8 бит, в числах real*8 отведено 11 бит, а в числах real*10 отведено 15 бит.

Тип

IEEE 754

Тип

Java

Число бит мантиссы

Число бит порядка

Сдвиг порядка

real*4

float

23+ подразумевается 1 ведущий бит

8

127

real*8

double

52+ подразумевается 1 ведущий бит

11

1023

real*10

-

64

15

16383


Чему равны минимальное и максимальное по модулю числа при их базовом представлении?

Минимальное значение достигается при минимальном порядке и всех нулевых битах мантиссы (за исключением ведущего), то есть при m=1 и p=1. Значит, минимальное значение равно 21-d.

Максимальное значение достигается при максимальном порядке и всех единичных битах мантиссы, то есть при m≈2 и p= pmax-1. Значит, максимальное значение примерно равно

При значениях порядка в диапазоне от 1 до pmax-1 базовое представление позволяет закодировать

В случае, когда порядок равен 0 или pmax, используется особое представление чисел, несколько отличающееся от базового.

Если все биты порядка равны 0, но мантисса отлична от нуля, то порядок считается равным 1 (а не 0), а вместо единицы в качестве подразумеваемой ведущей цифры используется ноль. Это ненормализованное представление чисел. Максимальное значение мантиссы в этом случае на младший бит мантиссы меньше 1. Так что максимальное значение числа в особой форме представления равно (1- младший бит мантиссы)*21-d. То есть верхний предел диапазон изменения чисел в этом представлении смыкается с нижним диапазоном изменения чисел в базовом представлении.

Минимальное ненулевое значение мантиссы в особом представлении равно 2-n, где n – число бит мантиссы после двоичной точки.

Минимальное отличное от нуля положительное число для некоторого типа чисел с плавающей точкой равно 21-d-n.

Таким образом, особое представление позволяет закодировать

Специальный случай особого представления – когда и порядок и мантисса равны нулю. Это значение обозначает машинный ноль. В соответствии со стандартом IEEE 754 имеются +0 и -0. Но во всех известных автору языках программирования +0 и -0 при вводе-выводе и сравнении чисел отождествляются.

Нечисловое представление соответствует случаю, когда p=pmax, то есть все биты порядка равны 1. Такое “число” в зависимости от значения мантиссы обозначает одно из трех специальных нечиселовых значений, которые обозначают как Inf (Infinity - “бесконечность”), NaN (Not a Number - “не число”), Ind (Indeterminate – “неопределённость”). Эти значения появляются при переполнениях и неопределённостях в вычислениях. Например, при делении 1 на 0 получается Inf, а при делении 0 на 0 получается Ind. Значение NaN может получаться при преобразовании строки в число, взятии логарифма от отрицательного числа, тригонометрической функции от бесконечности и т.п.

Значение Inf соответствует нулевым битам мантиссы.Согласно IEEE 754 бесконечность имеет знак. Если знаковый бит 0 это + Inf, если знаковый бит 1 это –Inf.

Значение Ind кодируется единицей в знаковом бите и битами мантиссы, равными 0 во всех разрядах кроме старшего (реального, а не подразумеваемого), где стоит 1. Все остальные сочетания знакового бита и мантиссы отведены под величины NaN. Значения NaN бывают двух типов – вызывающие возбуждение сигнала о переполнении (Signaling NaN) и не вызывающие (Quiet NaN). Значения обоих этих типов могут быть “положительными” (знаковый бит равен нулю) и “отрицательными” (знаковый бит равен единице).

В современных языках программирования поддерживается только часть возможностей, реализованных в процессорах в соответствии со стандартом IEEE 754. Например, в Java значения бесконечности различаются как по знаку, так и по типу: имеются Float.NEGATIVE_INFINITY, Float.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY.

Но значение Ind вообще не употребляется и отождествляется с NaN, хотя Float.NaN и Double.NaN различаются.

Числа в формате с плавающей точкой занимают следующие диапазоны значений:

Название значения

s (знак)

p (порядок)

m (мантисса)

-NaN

(отрицательное не-число)

1

11..11

11..11

:

10..01

Indeterminate (неопределённость)

1

11..11

10..00

Signaling -NaN (отрицательное не-число, возбуждающее ошибку)

1

11..11

01..11

:

00..01

-Infinity

(отрицательное переполнение, плюс бесконечность)

1

11..11

00..00

Отрицательное нормализованное

1

11..10

:

00..01

11..11

:

00..00

Отрицательное ненормализованное

1

00..00

11..11

:

00..01

-0

1

00..00

00..00

+0

0

00..00

00..00

Положительное ненормализованное

0

00..00

00..01

:

11..11

Положительное нормализованное

0

00..01

:

11..10

00..00

:

11..11

+Infinity

(положительное переполнение, плюс бесконечность)

0

11..11

00..00

Signaling +NaN (положительное не-число, возбуждающее ошибку)

0

11..11

00..01

:

01..11

Quiet +NaN

(положительное не-число)

0

11..11

10..00

:

11..11

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

Аналогичные операции существуют и для значений типа long и double:

Float.intBitsToFloat(значение типа int)

Double.longBitsToDouble(значение типа long)

Float.floatToIntBits(значение типа float)

Double.doubleToLongBits(значение типа double)

Например, Float.intBitsToFloat(0x7F7FFFFF) даст значение, равное Float.MAX_VALUE, Float.intBitsToFloat(0x7F800000) – значение Float.POSITIVE_INFINITY,

Float.intBitsToFloat(0xFF800000) – значение Float.NEGATIVE_INFINITY.

Если аргумент метода Float.intBitsToFloat лежит в пределах от 0xF800001 до 0xF800001, результатом будет Float.NaN.

Следует подчеркнуть, что данные операции принципиально отличаются от “обычных” преобразований типов, например, из int в float или из double в long. При “обычных” преобразованиях значение числа не меняется, просто меняется форма хранения этого значения и, соответственно, наборы битов, которыми кодируется это значение. Причём может измениться размер ячейки (скажем, при преобразовании значений int в значения double). А при рассматриваемых в данном разделе операциях сохраняется набор бит и размер ячейки, но меняется тип, который приписывается этому набору.