Является ли IEEE 754-2008 детерминированным?

Если я начну с одинаковых значений и выполню одинаковые примитивные операции (сложение, умножение, сравнение и т. Д.) Для 64-разрядных значений IEEE 754-2008 с двойной точностью, получу ли я тот же результат, независимо от базовой машины?

Конкретнее: поскольку ECMAScript 2015 указывает, что числовые значения

значение примитива, соответствующее 64-битному двоичному формату IEEE 754-2008 с двойной точностью

могу ли я сделать вывод, что одни и те же операции здесь дают одинаковый результат, независимо от среды?

1 ответ

(Здесь много сносок, чтобы отпугнуть толпу, но они не влияют на ваши вопросы о ECMAScript.)

ИЭЭЭ 754

Если я начну с тех же значений и выполню те же примитивные операции (сложение, умножение, сравнение и т. д.) с 64-битными значениями двойной точности IEEE 754-2008, получу ли я тот же результат, независимо от базовой машины?

Да.

Стандарт IEEE 754-2008 (и IEEE 754-2019) точно определяет операции сложения, вычитания, умножения, деления и извлечения квадратного корня для всех значений с плавающей запятой, за исключением различий между различными значениями NaN.1 Реализации стандарта 2 согласуются со всеми входными данными. То же самое касается трехстороннего сравнения (<, = или >, определенных для чисел, включая бесконечности; вызывает исключение для NaN) или четырехстороннего сравнения (<, =, > или неупорядоченного, определенного для всех значений с плавающей запятой). включая NaN).

Эти пять арифметических операций не только точно определены для всех входных данных, но и для числовых входных данных точно определены для правильного округления: операция сложения с плавающей запятой ⊕ определена для получения fl( + ), что является результатом округления сумма вещественных чисел + в соответствии с текущим режимом округления, 3 , что по умолчанию возвращает ближайшее число с плавающей запятой или, в случае ничьей, ближайшее число, у которого младшая значащая цифра четная.

ECMAScript 2015 (и 2021)

Более конкретно: поскольку ECMAScript 2015 указывает, что числовые значения

примитивное значение, соответствующее 64-битному двоичному формату двойной точности IEEE 754-2008

Могу ли я заключить, что одни и те же операции дают здесь один и тот же результат, независимо от среды?

Да.

Операции , , и над числами в ECMAScript 2015 точно определены для всех входных данных в соответствии со стандартом IEEE 754.4 Например, в определении сложения в ECMAScript 2015 конкретно указано:

Результат сложения определяется по правилам двоичной арифметики двойной точности IEEE 754-2008:

Определение добавления в ECMAScript 2021 остается практически таким же, но вместо этого обновлено для ссылки на IEEE 754-2019:

Абстрактная операция Number::add принимает аргументы x (число) и y (число). Он выполняет сложение в соответствии с правилами двоичной арифметики двойной точности IEEE 754-2019, производя сумму своих аргументов.

Точно так же равенство в ECMAScript 2015 и равенство в ECMAScript 2021 определяются в соответствии с IEEE 754-2008 и IEEE 754-2019, хотя и без явного цитирования.Реляционные операторы в ECMAScript 2015 и реляционные операторы в ECMAScript 2021 реализуют понятие упорядоченного сравнения IEEE 754, возвращая falseкогда любой вход NaN и соответствующий порядок в противном случае.

в ECMAScript 2015 и ECMAScript 2021 разрешено возвращать определяемое реализацией приближение (с учетом ограничений, касающихся угловых случаев) к квадратному корню, даже несмотря на то, что IEEE 754 точно определяет операцию квадратного корня и делал это с самого начала в IEEE. 754-1985. Однако с практической точки зрения крайне маловероятно, что реализация не сможет вернуть правильно округленный результат, как того требует IEEE 754.

Примечание. Многие операции , кроме четырех или пяти основных арифметических операций ( +, -, *, /; Math.sqrt) разрешены и, скорее всего, будут варьироваться от реализации к реализации. Например, одна реализация может использовать простую полиномиальную аппроксимацию для Math.log1p, в то время как другой может использовать набор приближений на основе таблицы, что дает немного разные результаты для некоторых входных данных. Это иногда используется как вектор для снятия отпечатков пальцев браузера. Но любое приближение , которое вы реализуете, используя только основные арифметические операции, будет согласовано во всех реализациях ECMAScript.

Оператор в ECMAScript 2015 и ECMAScript 2021 точно определен для всех входных данных, но не согласуется с операцией остатка IEEE 754: ECMAScript использует усеченное деление, тогда как остаток IEEE 754 использует округление до ближайшего/привязки к четному.(ECMAScript %является fmodв C, тогда как остаток IEEE 754 remainderв С.)

Другие языки

Приведенные выше ответы не всегда применимы к другим языкам. Например, подавляющее большинство реализаций C обеспечивают арифметику IEEE 754 binary64 для и арифметику binary32 для , но стандарт C разрешает им использовать разные арифметические правила в выражениях при условии, что они указывают, какие правила используются с помощью макроса:

За исключением присваивания и приведения (которые удаляют весь дополнительный диапазон и точность), значения, получаемые операторами с плавающими операндами и значениями, подлежащими обычным арифметическим преобразованиям и плавающими константами, оцениваются в формате, диапазон и точность которого могут быть больше, чем требуется тип. Использование форматов оценки характеризуется определяемым реализацией значением:

  • -1неопределенный;
  • оценивайте все операции и константы только до диапазона и точности типа;
  • 1оценивать операции и константы типа floatи к диапазону и точности типа, оценивайте операции и константы к диапазону и точности типа;
  • оценивайте все операции и константы в диапазоне и точности long doubleтип.

Все остальные отрицательные значения характеризуют поведение, определяемое реализацией.

(C11, §5.2.4.2.2: Характеристики плавающих типов <float.h>, ¶9, с. 30)

Это означает, что когда реализация определяет 2, функция вроде

      double
naive_fma(double x, double y, double z)
{
    return x*y + z;
}

будет реализовано так, как если бы было написано:

      double
naive_fma(double x, double y, double z)
{
    return (long double)x*z + z;
}

Реализации C на архитектуре Intel IA-32 («i386») часто работают следующим образом: они используют модуль Intel x87 с плавающей запятой для вычисления выражений в 80-битной двоичной арифметике с плавающей запятой с 64-битной точностью («двойной с расширенной точностью»), а затем округлить до IEEE 754 binary64 везде, где результаты хранятся в переменной, передаются как аргумент или явно приводятся к double. 5

Однако такой подход к вычислению выражений не разрешен в ECMAScript, так что вам не о чем беспокоиться. Реализация C, которая работает путем компиляции в ECMAScript очевидным способом, просто определила бы FLT_EVAL_METHODбыть 0.


1 Содержимое полезной нагрузки NaN может варьироваться от реализации к реализации. Однако, является ли результат NaN и является ли результат NaN сигнальным или тихим, определяется стандартом.

2 Некоторое оборудование также обеспечивает нестандартные режимы работы, такие как сброс в ноль, что приводит к тому, что операции возвращают нуль, когда в соответствии с семантикой IEEE 754 они возвращали бы ненормальные числа; в этом случае аппаратное обеспечение не является реализацией стандарта. Если вы включите эти режимы, вы можете получить разные ответы, но обычно они не включены, и они нарушают теоремы, часто принимаемые численными алгоритмами, такими как лемма Стербенца , поэтому они используются только в специализированных приложениях.ECMAScript не поддерживает сбрасывание в ноль или другие нестандартные режимы работы, а также не поддерживает какие-либо реализации, о которых я знаю: вы можете полагаться на постепенное снижение значения ниже нормы, как это определено в IEEE 754.

3IEEE 754 позволяет реализации поддерживать режим динамического округления с четырьмя определенными направлениями округления: до ближайшего/привязки к четному, вверх (в сторону положительной бесконечности), вниз (в сторону отрицательной бесконечности) и к нулю. В некоторых средах программы могут запрашивать и изменять текущий режим округления, например, в C с помощью fegetroundа также fesetround, хотя поддержка инструментальной цепочки для этого часто ограничена и служит в основном для внесения небольших возмущений в числовые алгоритмы для проверки резких изменений в выходных данных, указывающих на проблемы в алгоритме.ECMAScript не поддерживает изменение режима округления и не поддерживает какие-либо реализации, о которых мне известно: вам нужно иметь дело только с округлением до ближайшего/связями по умолчанию по умолчанию.

4 Семантика ECMAScript различает только одно значение NaN; в ECMAScript нет понятия полезной нагрузки NaN или сигнализации по сравнению с тихим NaN. Под капотом два NaN могут храниться с разными битовыми шаблонами, но ECMAScript не различает их семантически и не дает возможности различать их или исследовать битовые шаблоны под капотом.

5 Вычисление выражений с более высокой точностью может иногда приводить к ошибкам из-за двойного округления — например, если добавить 0x1p+53 и 0x1,7ffp+1, первое округление до 64-битной точности даст 0x1,000000000000018p+53, поэтому второе округление до 53-битная точность дает 0x1.00000000000002p+53, тогда как правильно округленная сумма с 53-битной точностью равна 0x1.00000000000001p+53. Так зачем это делать? На практике это почти всегда приводит к большей точности в численных алгоритмах за счет использования более высокой промежуточной точности: вы можете позволить себе потерять тысячи ulps с 64-битной точностью и все же получить ответ с точностью до пары ulps для 53-битной точности.

Другие вопросы по тегам