Введение
Стандарт IEEE для арифметики с плавающей запятой
Стандарт IEEE для арифметики с плавающей запятой (IEEE 754) — технический стандарт для арифметики с плавающей запятой, разработанный в 1985 году Институтом инженеров электротехники и электроники (IEEE). Стандарт решил множество проблем, возникавших из-за разнообразия реализаций арифметики с плавающей запятой, что затрудняло их надежное и переносимое использование. Многие аппаратные блоки вычислений с плавающей запятой используют стандарт IEEE 754. Стандарт определяет:
арифметические форматы: наборы двоичных и десятичных данных с плавающей запятой, состоящие из конечных чисел (включая знаковые нули и субнормальные числа), бесконечностей и специальных значений «не число» (NaN);
форматы обмена: кодировки (битовые строки), которые могут использоваться для обмена данными с плавающей запятой в эффективной и компактной форме;
правила округления: свойства, которые должны выполняться при округлении чисел во время арифметических операций и преобразований;
операции: арифметические и другие операции (например, тригонометрические функции) над арифметическими форматами;
обработка исключений: индикация исключительных ситуаций (например, деление на ноль, переполнение и т. д.). IEEE 754 2008, опубликованный в августе 2008 года, включает почти весь оригинальный стандарт IEEE 754 1985 года, а также стандарт IEEE 854 1987 года для арифметики с плавающей запятой, не зависящей от системы счисления. Текущая версия, IEEE 754 2019, была опубликована в июле 2019 года. Это незначительное обновление предыдущей версии, включающее в основном уточнения, исправления ошибок и новые рекомендуемые операции.
arithmetic formats: sets of binary and decimal floating point data, which consist of finite numbers (including signed zeros and subnormal numbers), infinities, and special "not a number" values (NaNs)
interchange formats: encodings (bit strings) that may be used to exchange floating point data in an efficient and compact form
rounding rules: properties to be satisfied when rounding numbers during arithmetic and conversions
operations: arithmetic and other operations (such as trigonometric functions) on arithmetic formats
exception handling: indications of exceptional conditions (such as division by zero, overflow, etc.) IEEE 754 2008, published in August 2008, includes nearly all of the original IEEE 754 1985 standard, plus the IEEE 854 1987 Standard for Radix Independent Floating Point Arithmetic. The current version, IEEE 754 2019, was published in July 2019. It is a minor revision of the previous version, incorporating mainly clarifications, defect fixes and new recommended operations.
История
Первый стандарт для арифметики с плавающей запятой, IEEE 754 1985, был опубликован в 1985 году. Он охватывал только двоичную арифметику с плавающей запятой. Новая версия, IEEE 754 2008, была опубликована в августе 2008 года после семилетнего процесса пересмотра, возглавляемого Дэном Зурасом и редактируемого Майком Коулишоу. Она заменила как IEEE 754 1985 (двоичная арифметика с плавающей запятой), так и IEEE 854 1987 «Стандарт для арифметики с плавающей запятой, независимой от системы счисления». Двоичные форматы из исходного стандарта включены в этот новый стандарт вместе с тремя новыми базовыми форматами: одним двоичным и двумя десятичными. Для соответствия текущему стандарту реализация должна поддерживать как минимум один из базовых форматов как в качестве арифметического формата, так и в качестве формата обмена данными. Международный стандарт ISO/IEC/IEEE 60559:2011 (с содержанием, идентичным IEEE 754 2008) был одобрен для принятия в рамках ISO/IEC JTC 1/SC 25 в соответствии с Соглашением ISO/IEEE PSDO и опубликован. Текущая версия, IEEE 754 2019, опубликованная в июле 2019 года, является производной от IEEE 754 2008 и заменяет его после процесса пересмотра, начатого в сентябре 2015 года, под председательством Дэвида Г. Хауга и редактированием Майка Коулишоу. Она включает в основном уточнения (например, totalOrder) и исправления ошибок (например, minNum), а также некоторые новые рекомендуемые операции (например, augmentedAddition). Международный стандарт ISO/IEC 60559:2020 (с содержанием, идентичным IEEE 754 2019) был одобрен для принятия в рамках ISO/IEC JTC 1/SC 25 и опубликован. Следующий запланированный пересмотр стандарта – в 2028 году.
Представление и кодирование в памяти
Некоторые числа могут иметь несколько возможных представлений с плавающей точкой. Например, если b = 10, а p = 7, то −12.345 может быть представлено как −12345×10−3, −123450×10−4 и −1234500×10−5. Однако для большинства операций, таких как арифметические, результат (значение) не зависит от представления входных данных. Для десятичных форматов любое представление допустимо, и множество этих представлений называется когортой. Когда результат может иметь несколько представлений, стандарт определяет, какой элемент когорты выбирается. Для двоичных форматов представление становится однозначным путем выбора наименьшего представимого экспонента, позволяющего точно представить значение. Далее, экспонент не представляется напрямую, но к нему добавляется смещение, так что наименьший представимый экспонент представляется как 1, а 0 используется для субнормальных чисел. Для чисел с экспонентом в нормальном диапазоне (поле экспоненты не состоит полностью из единиц или нулей), старший бит мантиссы всегда равен 1. Следовательно, старшую 1 можно подразумевать, а не явно указывать в кодировке памяти, и согласно стандарту, явно представленная часть мантиссы лежит между 0 и 1. Это правило называется соглашением о старшем бите, соглашением о неявном бите или соглашением о скрытом бите. Это правило позволяет двоичному формату иметь дополнительный бит точности. Соглашение о старшем бите нельзя использовать для субнормальных чисел, поскольку они имеют экспонент за пределами нормального диапазона экспонент и масштабируются наименьшим представленным экспонентом, как это делается для наименьших нормальных чисел. Из-за возможности множественного кодирования (по крайней мере, в форматах, называемых форматами обмена), NaN может содержать дополнительную информацию: бит знака (который не имеет значения, но может использоваться некоторыми операциями) и полезную нагрузку, предназначенную для диагностической информации, указывающей источник NaN (но полезная нагрузка может иметь и другие применения, например, упаковку NaN).
Расширенные и расширяемые форматы точности
Стандарт определяет необязательные форматы расширенной и расширяемой точности, обеспечивающие большую точность, чем базовые форматы. Формат расширенной точности расширяет базовый формат, используя большую точность и больший диапазон экспоненты. Формат расширяемой точности позволяет пользователю задавать точность и диапазон экспоненты. Реализация может использовать любое внутреннее представление для таких форматов; необходимо определить только их параметры (b, p и emax). Эти параметры однозначно описывают множество конечных чисел (комбинации знака, мантиссы и экспоненты для данного основания), которые могут быть представлены. Стандарт рекомендует, чтобы языковые стандарты предоставляли способ указания p и emax для каждого поддерживаемого основания b. Стандарт рекомендует, чтобы языковые стандарты и реализации поддерживали расширенный формат, имеющий большую точность, чем самый большой поддерживаемый базовый формат для каждого основания b. Для расширенного формата с точностью между двумя базовыми форматами диапазон экспоненты должен быть не меньше диапазона экспоненты следующего большего базового формата. Например, 64-битное число с расширенной точностью в двоичном формате должно иметь 'emax' не менее 16383. 80-битный расширенный формат x87 соответствует этому требованию. В исходном стандарте IEEE 754 1985 года также была концепция расширенных форматов, но без обязательной связи между emin и emax. Например, 80-битный формат Motorola 68881, где emin = −emax, был соответствующим расширенным форматом, но он стал несоответствующим в редакции 2008 года.
Форматы обмена
Форматы обмена предназначены для обмена данными с плавающей точкой, используя битовую строку фиксированной длины для заданного формата.
Бинарный
Для обмена числами с плавающей точкой в двоичном формате определены форматы обмена длиной 16 бит, 32 бита, 64 бита и любое кратное 32 битам, большее или равное 128. 16-битный формат предназначен для обмена или хранения небольших чисел (например, для графики). Схема кодирования для этих форматов двоичного обмена соответствует стандарту IEEE 754 1985: бит знака, за которым следуют w битов экспоненты, описывающих смещенный экспонент, и p–1 битов мантиссы. Ширина поля экспоненты для k-битного формата вычисляется как w = округление(4 * log2(k)) – 13. Существующие 64- и 128-битные форматы соответствуют этому правилу, но 16- и 32-битные форматы имеют больше битов экспоненты (5 и 8 соответственно), чем предписывает эта формула (3 и 7 соответственно). Как и в IEEE 754 1985, поле смещенного экспонента заполняется единицами для обозначения либо бесконечности (если поле мантиссы заполнено нулями), либо NaN (если поле мантиссы не заполнено нулями). Для NaN тихие NaN и сигнальные NaN различаются по наиболее значащему биту поля мантиссы, а полезная нагрузка содержится в остальных битах.
Десятичная
Для обмена десятичными числами с плавающей точкой определены форматы обмена, кратные 32 битам. Как и при двоичном обмене, схема кодирования для десятичных форматов обмена кодирует знак, показатель и мантиссу. Определены два различных способа кодирования на уровне битов, и обмен усложняется тем, что может потребоваться внешний индикатор используемого кодирования. Два варианта позволяют кодировать мантиссу как сжатую последовательность десятичных цифр с использованием плотно упакованной десятичной формы или, альтернативно, как двоичное целое число. Первый вариант более удобен для прямой аппаратной реализации стандарта, а второй – для программной эмуляции на двоичном компьютере. В любом случае, множество чисел (комбинации знака, мантиссы и показателя), которые могут быть закодированы, одинаково, а специальные значения (±ноль с минимальным показателем, ±бесконечность, тихие NaN и сигнальные NaN) имеют идентичные кодировки.
Правила округляния
Стандарт определяет пять правил округления. Первые два правила округляют до ближайшего значения, остальные называются округлениями с направлением.
Окружение до ближайшего
Округлять до ближайшего, при равенстве – к чётному: округляет до ближайшего значения; если число находится ровно посередине между двумя целыми числами, оно округляется до ближайшего числа с чётной младшей значащей цифрой. Округлять до ближайшего, при равенстве – от нуля: округляет до ближайшего значения; если число находится ровно посередине между двумя целыми числами, оно округляется до ближайшего большего (для положительных чисел) или меньшего (для отрицательных чисел) значения. В крайних случаях значение, модуль которого строго меньше, округляется до минимального или максимального конечного числа (в зависимости от знака значения). Любые числа с точно таким модулем считаются равными; этот выбор при равенстве можно представить как середину между и , которые, если бы экспонента не была ограничена, были бы следующими представимыми числами с плавающей точкой по величине. Числа с модулем строго больше k округляются до соответствующей бесконечности. "Округлять до ближайшего, при равенстве – к чётному" является значением по умолчанию для двоичной арифметики с плавающей точкой и рекомендуемым значением по умолчанию для десятичной. "Округлять до ближайшего, при равенстве – от нуля" требуется только для десятичных реализаций.
Направленные округления
Округление к 0 – направленное округление к нулю (также известное как усечение). Округление к +∞ – направленное округление к положительной бесконечности (также известное как округление вверх или потолок). Округление к −∞ – направленное округление к отрицательной бесконечности (также известное как округление вниз или пол).
Unless specified otherwise, the floating point result of an operation is determined by applying the rounding function on the infinitely precise (mathematical) result. Such an operation is said to be correctly rounded. This requirement is called correct rounding.
+ Пример округления до целых чисел с использованием правил IEEE 754
Unless specified otherwise, the floating point result of an operation is determined by applying the rounding function on the infinitely precise (mathematical) result. Such an operation is said to be correctly rounded. This requirement is called correct rounding.
Режим | Пример значения
------- | --------
+11.5 | +12.5
−11.5 | −12.5
До ближайшего, с округлением к чётному | +12.0 | +12.0 | −12.0 | −12.0
До ближайшего, с округлением от нуля | +12.0 | +13.0 | −12.0 | −13.0
К 0 | +11.0 | +12.0 | −11.0 | −12.0
К +∞ | +12.0 | +13.0 | −11.0 | −12.0
К −∞ | +11.0 | +12.0 | −12.0 | −13.0
Unless specified otherwise, the floating point result of an operation is determined by applying the rounding function on the infinitely precise (mathematical) result. Such an operation is said to be correctly rounded. This requirement is called correct rounding.
Если не указано иное, результат операции с плавающей точкой определяется применением функции округления к бесконечно точному (математическому) результату. Такая операция считается правильно округленной. Это требование называется корректным округлением.
Unless specified otherwise, the floating point result of an operation is determined by applying the rounding function on the infinitely precise (mathematical) result. Such an operation is said to be correctly rounded. This requirement is called correct rounding.
Сравнительные предикаты
Стандарт предоставляет предикаты сравнения для сопоставления одного числа с плавающей точкой с другим в поддерживаемом арифметическом формате. Любое сравнение с NaN считается неупорядоченным. −0 и +0 считаются равными.
Предкаты с общим порядком
Стандарт предоставляет предикат totalOrder, который определяет полный порядок канонических членов поддерживаемого арифметического формата. Этот предикат согласуется с предикатами сравнения (см. раздел), когда одно число с плавающей точкой меньше другого. Основные отличия заключаются в следующем:
NaN подлежит упорядочиванию. NaN рассматривается так, как будто у него большее абсолютное значение, чем у бесконечности (или любого другого числа с плавающей точкой). (−NaN < −Infinity; +Infinity < +NaN.) qNaN и sNaN рассматриваются так, как будто у qNaN большее абсолютное значение, чем у sNaN. (−qNaN < −sNaN; +sNaN < +qNaN.) Затем NaN сортируется в соответствии с полезной нагрузкой. В IEEE 754 2008 NaN с меньшей полезной нагрузкой рассматривается как имеющий меньшее абсолютное значение. В IEEE 754 2019 допустим любой порядок, определяемый реализацией. Отрицательный ноль рассматривается как меньший, чем положительный ноль. Если обе стороны сравнения ссылаются на одно и то же число с плавающей точкой, то число с меньшим показателем рассматривается как имеющее меньшее абсолютное значение. Следующие случаи не являются частью стандарта IEEE 754:
Зажатый: показатель результата слишком велик для целевого формата. По умолчанию к коэффициенту добавляются завершающие нули, чтобы уменьшить показатель до максимально допустимого значения. Если это невозможно (потому что это потребует больше цифр, чем может вместить целевой формат), возникает исключение переполнения. Округленный: коэффициент результата требует больше цифр, чем предоставляет целевой формат. Если при этом отбрасываются какие-либо ненулевые цифры, генерируется исключение неточности. Кроме того, операции, такие как квантование, когда один из операндов бесконечен или результат не соответствует целевому формату, также вызывают исключение недопустимой операции.
NaN is sortable. NaN is treated as if it had a larger absolute value than Infinity (or any other floating point numbers). (−NaN < −Infinity; +Infinity < +NaN.) qNaN and sNaN are treated as if qNaN had a larger absolute value than sNaN. (−qNaN < −sNaN; +sNaN < +qNaN.) NaN is then sorted according to the payload. In IEEE 754 2008, a NaN with a lesser payload is treated as having a lesser absolute value. In IEEE 754 2019, any implementation defined ordering is acceptable. Negative zero is treated as smaller than positive zero. If both sides of the comparison refer to the same floating point datum, the one with the lesser exponent is treated as having a lesser absolute value. which are not part of IEEE 754:
Clamped: a result's exponent is too large for the destination format. By default, trailing zeros will be added to the coefficient to reduce the exponent to the largest usable value. If this is not possible (because this would cause the number of digits needed to be more than the destination format) then an overflow exception occurs. Rounded: a result's coefficient requires more digits than the destination format provides. An inexact exception is signaled if any non zero digits are discarded. Additionally, operations like quantize when either operand is infinite, or when the result does not fit the destination format, will also signal invalid operation exception.
Подпись нулевая
В стандарте IEEE 754 ноль имеет знак, что означает существование как "положительного нуля" (+0), так и "отрицательного нуля" (−0). В большинстве сред выполнения положительный ноль обычно отображается как "0", а отрицательный ноль как " 0". Эти два значения считаются равными при числовых сравнениях, однако некоторые операции возвращают разные результаты для +0 и −0. Например, 1/(−0) возвращает отрицательную бесконечность, а 1/(+0) – положительную бесконечность (для сохранения тождества 1=1/(1/±∞) = ±∞). Другие распространенные функции с разрывом при x=0, которые могут по-разному обрабатывать +0 и −0, включают log(x), signum(x) и главный квадратный корень из y + xi для любого отрицательного числа y. Как и в любой схеме аппроксимации, операции с "отрицательным нулем" иногда могут приводить к недоразумениям. Например, в IEEE 754 не всегда верно, что , поскольку 0 = −0, но 1/0 ≠ 1/(−0).
Недостаточное количество
Поднормальные значения заполняют промежуток недополнения значениями, для которых абсолютное расстояние между ними такое же, как и для соседних значений непосредственно за пределами этого промежутка. Это улучшение по сравнению с прежней практикой, когда в промежутке недополнения просто использовался ноль, а результаты, приводящие к недополнению, заменялись нулем (обнуление). Современное аппаратное обеспечение для работы с числами с плавающей точкой обычно обрабатывает поднормальные (а также нормальные) значения и не требует программной эмуляции для поднормальных чисел.
Нана
IEEE 754 определяет специальное значение под названием "Не число" (NaN), которое должно возвращаться в результате определенных "некорректных" операций, таких как 0/0, ∞×0 или sqrt(−1). В общем случае, NaN распространяются, то есть большинство операций, включающих NaN, приведут к NaN, хотя функции, которые дают определенный результат для любого числа с плавающей точкой, дадут его и для NaN, например, NaN ^ 0 = 1. Существуют два вида NaN: по умолчанию – тихие NaN и, опционально – сигнальные NaN. Сигнальный NaN в любой арифметической операции (включая числовые сравнения) вызовет генерацию исключения "некорректной операции". Представление NaN, определенное стандартом, содержит некоторые незарезервированные биты, которые могут быть использованы для кодирования типа или источника ошибки, однако стандарт для такого кодирования отсутствует. Теоретически, сигнальные NaN могут использоваться средой выполнения для обозначения неинициализированных переменных или расширения чисел с плавающей точкой другими специальными значениями без замедления вычислений с обычными значениями, хотя такие расширения не являются распространенными.
Обоснование конструкции
Распространено заблуждение, что более эзотерические особенности стандарта IEEE 754, обсуждаемые здесь, такие как расширенные форматы, NaN, бесконечности, субнормальные числа и т. д., представляют интерес только для специалистов по численному анализу или для сложных численных приложений. На самом деле все обстоит с точностью до наоборот: эти особенности разработаны для обеспечения безопасных и надежных значений по умолчанию для программистов, не обладающих глубокими знаниями в области численных методов, в дополнение к поддержке сложных численных библиотек, создаваемых экспертами. Ключевой разработчик стандарта IEEE 754, Уильям Кахан, отмечает, что ошибочно " [считать] особенности стандарта IEEE 754 для двоичной арифметики с плавающей точкой, которые [не] признаются полезными только для численных экспертов. На самом деле все обстоит иначе. В 1977 году эти особенности были реализованы в Intel 8087 для охвата максимально широкого рынка. Анализ ошибок показывает, как разрабатывать арифметику с плавающей точкой, такую как IEEE 754, умеренно устойчивую к неизбежному невежеству программистов". Специальные значения, такие как бесконечность и NaN, гарантируют алгебраическую полноту арифметики с плавающей точкой: каждая операция с плавающей точкой дает однозначно определенный результат и не вызовет – по умолчанию – прерывания или ловушки машины. Более того, выбор специальных значений, возвращаемых в исключительных случаях, был сделан таким образом, чтобы во многих ситуациях получать правильный ответ. Например, при использовании арифметики IEEE 754, непрерывные дроби, такие как R(z) := 7 − 3/[z − 2 − 1/(z − 7 + 10/[z − 2 − 2/(z − 3)])], будут давать правильный результат для всех входных данных, поскольку потенциальное деление на ноль, например, при z = 3, корректно обрабатывается путем выдачи +бесконечности, и, следовательно, такие исключения можно безопасно игнорировать. Как отметил Кахан, необработанная ловушка, возникшая после переполнения при преобразовании числа с плавающей точкой в 16-битное целое число, которая привела к потере ракеты Ariane 5, не произошла бы при использовании стандартной политики работы с плавающей точкой IEEE 754. Кахан отмечает следующее относительно логической структуры 80-битного формата x87: "Этот расширенный формат предназначен для использования, с пренебрежимо малой потерей производительности, во всех операциях, кроме самых простых, с операндами float и double. Например, его следует использовать для временных переменных в циклах, реализующих рекуррентные соотношения, такие как вычисление полиномов, скалярные произведения, частные и непрерывные дроби. Он часто предотвращает преждевременное переполнение или потерю значимости, которые могут испортить простые алгоритмы". Вычисление промежуточных результатов в расширенном формате с высокой точностью и расширенным экспонентом имеет прецеденты в исторической практике научных вычислений и в разработке научных калькуляторов; например, финансовые калькуляторы Hewlett Packard выполняли арифметические и финансовые операции с тремя дополнительными значащими цифрами, чем хранили или отображали.
Замена исключения обработки
Стандарт рекомендует опциональную обработку исключений в различных формах, включая предварительную подстановку пользовательских значений по умолчанию, а также ловушки (исключения, изменяющие поток управления) и другие модели обработки исключений, прерывающие выполнение, такие как try/catch. Ловушки и другие механизмы обработки исключений остаются необязательными, как и в IEEE 754 1985.
Оценка выражения
Стандарт рекомендует, как языковые стандарты должны определять семантику последовательностей операций, и указывает на тонкости буквального толкования и оптимизаций, изменяющих значение результата. В отличие от этого, предыдущая версия стандарта 1985 года оставляла некоторые аспекты языкового интерфейса неопределенными, что приводило к несогласованному поведению между компиляторами или различным уровням оптимизации в оптимизирующем компиляторе. Языки программирования должны предоставлять пользователю возможность указывать минимальную точность для промежуточных вычислений выражений для каждой системы счисления. В стандарте это называется preferredWidth, и должна быть возможность устанавливать это значение для каждого блока кода. Промежуточные вычисления в выражениях должны выполняться, а любые временные значения сохраняться, с использованием максимальной ширины операндов или preferredWidth, если он задан. Таким образом, например, компилятор, предназначенный для аппаратного обеспечения с плавающей точкой x87, должен иметь возможность указать, что промежуточные вычисления должны выполняться в формате двойной расширенной точности. При вычислении последующих выражений всегда должно использоваться сохраненное значение переменной, а не ее предыдущее значение до округления и присваивания.
Воспроизводимость
IEEE 754 1985 версия стандарта допускала множество вариаций в реализации (например, кодирование некоторых значений и обнаружение определенных исключений). IEEE 754 2008 уменьшил количество этих допущений, но некоторые вариации всё ещё сохраняются (особенно для двоичных форматов). Положение о воспроизводимости рекомендует, чтобы языковые стандарты предоставляли средства для написания воспроизводимых программ (то есть программ, которые будут выдавать один и тот же результат на всех реализациях языка) и описывает, что необходимо сделать для достижения воспроизводимости результатов.
Шестидесятизначные буквы
Стандарт рекомендует предоставлять преобразования из внешних и во внешние последовательности шестнадцатеричных значащих цифр, основанные на шестнадцатеричных литералах с плавающей точкой стандарта C99. Такой литерал состоит из необязательного знака (+ или -), индикатора "0x", шестнадцатеричного числа с точкой или без нее, индикатора экспоненты "p" и десятичной экспоненты с необязательным знаком. Синтаксис не чувствителен к регистру. Десятичная экспонента масштабируется в степенях 2, поэтому, например, 0x0.1p4 равно 1/256.