IEEE 754-1985 [1] — исторический отраслевой стандарт для представления чисел с плавающей точкой в компьютерах , официально принятый в 1985 году и замененный в 2008 году IEEE 754-2008 , а затем снова в 2019 году незначительной редакцией IEEE 754-2019 . [2] В течение своих 23 лет он был наиболее широко используемым форматом для вычислений с плавающей точкой. Он был реализован в программном обеспечении в виде библиотек с плавающей точкой и в аппаратном обеспечении в инструкциях многих центральных процессоров и FPU . Первой интегральной схемой , реализовавшей проект того, что должно было стать IEEE 754-1985, была Intel 8087 .
IEEE 754-1985 представляет числа в двоичной системе счисления , давая определения для четырех уровней точности, из которых наиболее часто используются два:
Стандарт также определяет представления для положительной и отрицательной бесконечности , « отрицательного нуля », пять исключений для обработки недопустимых результатов, таких как деление на ноль , специальные значения, называемые NaN, для представления этих исключений, ненормальные числа для представления чисел, меньших, чем показано выше, и четыре режима округления .
Числа с плавающей точкой в формате IEEE 754 состоят из трех полей: знакового бита , смещенной экспоненты и дроби. Следующий пример иллюстрирует значение каждого из них.
Десятичное число 0,15625 10, представленное в двоичном виде, равно 0,00101 2 (то есть 1/8 + 1/32). (Нижние индексы указывают основание системы счисления .) По аналогии с научной записью , где числа записываются так, чтобы иметь одну ненулевую цифру слева от десятичной точки, мы переписываем это число так, чтобы оно имело один бит 1 слева от «двоичной точки». Мы просто умножаем на соответствующую степень числа 2, чтобы компенсировать сдвиг битов влево на три позиции:
Теперь мы можем прочитать дробь и показатель степени: дробь равна 0,012 , а показатель степени равен −3.
Как показано на рисунках, в представлении IEEE 754 этого числа имеются три поля:
IEEE 754 добавляет смещение к показателю, так что числа во многих случаях можно удобно сравнивать с помощью того же оборудования, которое сравнивает целые числа со знаком в дополнительном коде . При использовании смещенного показателя меньшее из двух положительных чисел с плавающей точкой будет «меньше» большего, следуя тому же порядку, что и для целых чисел со знаком и величиной . Если два числа с плавающей точкой имеют разные знаки, сравнение знака и величины также работает со смещенными показателями. Однако, если оба числа с плавающей точкой со смещенным показателем отрицательны, то порядок должен быть обратным. Если показатель степени был представлен, скажем, как число в дополнительном коде, сравнение для определения того, какое из двух чисел больше, было бы не таким удобным.
Начальный бит 1 опущен, поскольку все числа, кроме нуля, начинаются с начальной 1; начальная 1 подразумевается и на самом деле не нуждается в хранении, что дает дополнительный бит точности «бесплатно».
Число ноль представлено особым образом:
Представления чисел, описанные выше, называются нормализованными, что означает, что неявная ведущая двоичная цифра равна 1. Чтобы уменьшить потерю точности при возникновении потери значимости , IEEE 754 включает возможность представления дробей, меньших, чем это возможно в нормализованном представлении, делая неявную ведущую цифру равной 0. Такие числа называются ненормализованными . Они не включают столько значащих цифр, сколько нормализованное число, но допускают постепенную потерю точности, когда результат операции не равен нулю, но слишком близок к нулю, чтобы быть представленным нормализованным числом.
Ненормальное число представлено смещенной экспонентой из всех нулевых бит, что представляет экспоненту −126 при одинарной точности (не −127) или −1022 при двойной точности (не −1023). [3] Напротив, наименьшая смещенная экспонента, представляющая нормальное число, равна 1 (см. примеры ниже).
Поле смещенной экспоненты заполняется всеми единичными битами, чтобы указать либо на бесконечность, либо на недопустимый результат вычисления.
Положительная и отрицательная бесконечность представлены следующим образом:
Некоторые операции арифметики с плавающей точкой недопустимы, например, извлечение квадратного корня из отрицательного числа. Действие по достижению недопустимого результата называется исключением с плавающей точкой. Исключительный результат представлен специальным кодом, называемым NaN, что означает « Не число ». Все NaN в IEEE 754-1985 имеют следующий формат:
Точность определяется как минимальная разница между двумя последовательными представлениями мантиссы; таким образом, она является функцией только мантиссы; в то время как зазор определяется как разница между двумя последовательными числами. [4]
Числа одинарной точности занимают 32 бита. В одинарной точности:
Некоторые примеры значений диапазона и зазора для заданных показателей степеней с одинарной точностью:
Например, 16 777 217 не может быть закодировано как 32-битное число с плавающей точкой, поскольку оно будет округлено до 16 777 216. Однако все целые числа в пределах представимого диапазона, которые являются степенью 2, могут быть сохранены в 32-битном числе с плавающей точкой без округления.
Числа двойной точности занимают 64 бита. В двойной точности:
Некоторые примеры значений диапазона и промежутка для заданных показателей степени с двойной точностью:
Стандарт также рекомендует использовать расширенный формат(ы) для выполнения внутренних вычислений с более высокой точностью, чем требуется для конечного результата, чтобы минимизировать ошибки округления: стандарт определяет только минимальные требования к точности и показателю для таких форматов. Расширенный формат x87 80-бит является наиболее часто реализуемым расширенным форматом, который соответствует этим требованиям.
Вот несколько примеров представлений IEEE 754 с одинарной точностью:
Каждая возможная комбинация битов — это либо NaN, либо число с уникальным значением в аффинно расширенной системе действительных чисел с соответствующим порядком, за исключением двух комбинаций битов для отрицательного нуля и положительного нуля, которые иногда требуют особого внимания (см. ниже). Двоичное представление обладает особым свойством, заключающимся в том, что, за исключением NaN, любые два числа можно сравнивать как целые числа со знаком и величиной ( применяются проблемы с порядком байтов ). При сравнении как целых чисел с дополнением до 2 : если биты знака различаются, отрицательное число предшествует положительному числу, поэтому дополнение до 2 дает правильный результат (за исключением того, что отрицательный ноль и положительный ноль следует считать равными). Если оба значения положительны, сравнение с дополнением до 2 снова дает правильный результат. В противном случае (два отрицательных числа) правильный порядок FP противоположен порядку с дополнением до 2.
Ошибки округления, присущие вычислениям с плавающей точкой, могут ограничивать использование сравнений для проверки точного равенства результатов. Выбор приемлемого диапазона — сложная тема. Распространенным методом является использование значения эпсилон сравнения для выполнения приблизительных сравнений. [6] В зависимости от того, насколько снисходительны сравнения, распространенными значениями являются 1e-6
или 1e-5
для одинарной точности, и 1e-14
для двойной точности. [7] [8] Другим распространенным методом является ULP, который проверяет, какова разница в последних цифрах, эффективно проверяя, на сколько шагов отличаются два значения. [9]
Хотя отрицательный ноль и положительный ноль обычно считаются равными для целей сравнения, некоторые реляционные операторы языков программирования и подобные конструкции рассматривают их как разные. Согласно спецификации языка Java [10] , операторы сравнения и равенства рассматривают их как равные, но и различают их (официально начиная с версии Java 1.1, но фактически с 1.1.1), как и методы сравнения , и даже классов и .Math.min()
Math.max()
equals()
compareTo()
compare()
Float
Double
Стандарт IEEE предусматривает четыре различных режима округления: первый используется по умолчанию, остальные называются направленными округлениями .
Стандарт IEEE использует (и расширяет) аффинно расширенную систему действительных чисел с отдельными положительными и отрицательными бесконечностями. Во время разработки было предложено включить в стандарт проективно расширенную систему действительных чисел с одной беззнаковой бесконечностью, предоставив программистам возможность выбора режима. Однако в интересах снижения сложности окончательного стандарта проективный режим был исключен. Сопроцессоры с плавающей точкой Intel 8087 и Intel 80287 поддерживают этот проективный режим. [11] [12] [13]
Должны быть предусмотрены следующие функции:
NaN
для любого x (включая NaN
).copysign(x,y)
возвращает x со знаком y, поэтому abs(x)
равно copysign(x,1.0)
. Это одна из немногих операций, которая работает с NaN способом, напоминающим арифметику. Функция copysign
является новой в стандарте C99.scalb(y, N)
logb(x)
finite(x)
предикат для «x — конечное значение», эквивалентный −Inf < x < Infisnan(x)
предикат для «x is a NaN», эквивалентный «x ≠ x»x <> y
( x .LG. y
в Фортране ), который, как оказалось, ведет себя иначе, чем NOT(x = y)
( x .NE. y
в Фортране, x != y
в Си ) [14] из-за NaN.unordered(x, y)
истинно, когда «x не упорядочен с y», т.е. либо x, либо y является NaN.class(x)
nextafter(x,y)
возвращает следующее представимое значение из x в направлении к yВ 1976 году Intel начала разработку сопроцессора с плавающей точкой . [15] [16] Intel надеялась продать чип, содержащий хорошие реализации всех операций, имеющихся в самых разных библиотеках математического программного обеспечения. [15] [17]
Джон Палмер, который руководил проектом, считал, что усилия должны быть подкреплены стандартом, унифицирующим операции с плавающей точкой на разных процессорах. Он связался с Уильямом Каханом из Калифорнийского университета , который помог улучшить точность калькуляторов Hewlett-Packard . Кахан предложил Intel использовать плавающую точку VAX от Digital Equipment Corporation (DEC). Первый VAX, VAX-11/780, только что вышел в конце 1977 года, и его плавающая точка была высоко оценена. Однако, стремясь вывести свой чип на максимально широкий рынок, Intel хотела получить наилучшую возможную плавающую точку, и Кахан продолжил составлять спецификации. [15] Кахан изначально рекомендовал, чтобы основание с плавающей точкой было десятичным [18] [ ненадежный источник? ], но аппаратная конструкция сопроцессора была слишком продвинута, чтобы внести это изменение.
Работа в Intel беспокоила других поставщиков, которые организовали стандартизацию, чтобы обеспечить «равные условия». Кахан посетил второе заседание рабочей группы по стандартам IEEE 754, состоявшееся в ноябре 1977 года. Впоследствии он получил разрешение от Intel выдвинуть проект предложения, основанный на его работе для их сопроцессора; ему было разрешено объяснить детали формата и его обоснование, но не что-либо, связанное с архитектурой реализации Intel. Проект был написан совместно с Джеромом Куненом и Гарольдом Стоуном и изначально был известен как «предложение Кэхана-Кунена-Стоуна» или «формат KCS». [15] [16] [17] [19]
Поскольку 8-битная экспонента была недостаточно широкой для некоторых операций, требуемых для чисел двойной точности, например, для хранения произведения двух 32-битных чисел, [20] и предложение Кахана, и контрпредложение DEC использовали 11 бит, как проверенный временем 60-битный формат с плавающей точкой CDC 6600 1965 года. [16] [19] [21] Предложение Кахана также предусматривало бесконечности, которые полезны при работе с условиями деления на ноль; нечисловые значения, которые полезны при работе с недопустимыми операциями; ненормальные числа , которые помогают смягчить проблемы, вызванные потерей значимости; [19] [22] [23] и более сбалансированное смещение экспоненты , которое может помочь избежать переполнения и потери значимости при взятии обратной величины числа. [24] [25]
Еще до того, как проект стандарта был одобрен, его уже внедрили несколько производителей. [26] [27] Intel 8087, анонсированный в 1980 году, стал первым чипом, реализовавшим проект стандарта.
В 1980 году уже был выпущен чип Intel 8087 [28], но DEC по-прежнему выступала против ненормальных чисел, в частности из-за проблем с производительностью, а также потому, что это дало бы DEC конкурентное преимущество в виде стандартизации на основе формата DEC.
Споры по поводу постепенного подтока продолжались до 1981 года, когда эксперт, нанятый DEC для его оценки, выступил против несогласных. DEC провел исследование, чтобы продемонстрировать, что постепенное подтока было плохой идеей, но исследование пришло к противоположному выводу, и DEC сдалась. В 1985 году стандарт был ратифицирован, но годом ранее он уже стал фактическим стандартом, внедренным многими производителями. [16] [19] [5]