Является ли сложение с плавающей точкой коммутативным в C++?

Для значений с плавающей запятой гарантируется, что a + b == b + a?

Я считаю, что это гарантировано в IEEE754, однако стандарт C++ не определяет, что IEEE754 должен использоваться. Единственный релевантный текст, кажется, из [expr.add]#3:

Результатом бинарного оператора + является сумма операндов.

Математическая операция "сумма" является коммутативной. Однако математическая операция "сумма" также ассоциативна, тогда как сложение с плавающей запятой определенно не ассоциативно. Итак, мне кажется, что мы не можем сделать вывод, что коммутативность "суммы" в математике означает, что эта цитата определяет коммутативность в C++.

4 ответа

Решение

Даже не требуется, чтобы a + b == a + b, Одно из подвыражений может содержать результат сложения с большей точностью, чем другое, например, когда использование нескольких сложений требует, чтобы одно из подвыражений было временно сохранено в памяти, когда другое подвыражение может храниться в регистре (с более высокой точностью).

Если a + b == a + b не гарантируется, a + b == b + a не может быть гарантировано Если a + b не нужно возвращать одно и то же значение каждый раз, а значения разные, одно из них обязательно не будет равно одной конкретной оценке b + a,

Нет, язык C++ обычно не предъявляет таких требований к оборудованию. Определяется только ассоциативность операторов.

Все виды сумасшедших вещей случаются в арифметике с плавающей точкой. Возможно, на некоторой машине добавление нуля к ненормальному числу производит ноль. Можно предположить, что машина может избежать обновления памяти в случае добавления регистра с нулевым значением в ненормированный в памяти. Возможно, что действительно тупой компилятор всегда помещает LHS в память, а RHS в регистр.

Обратите внимание, однако, что машина с некоммутативным сложением должна была бы конкретно определить, как выражения отображаются на инструкции, если вы собираетесь контролировать, какую операцию вы получаете. Левая сторона входит в первый операнд машины или второй?

Такая спецификация ABI, в которой упоминается конструкция выражений и инструкций на одном дыхании, была бы довольно патологической.

Стандарт C++ определенно не гарантирует IEEE 754. Библиотека имеет некоторую поддержку IEC 559 (которая в основном является версией стандарта IEEE 754 IEC), поэтому вы можете проверить, использует ли базовая реализация IEEE 754/IEC 559 (и когда это произойдет, вы можете зависеть от того, что он гарантирует, конечно).

По большей части стандарты C и C++ предполагают, что такие базовые операции будут реализованы, однако базовое оборудование работает. Для чего-то такого же общего, как IEEE 754, они позволят вам определить, присутствует ли он, но все же не требуют этого.

Сложение с любой заданной точностью является коммутативным, за исключением полезной нагрузки NaN для реализации C++ с использованием математики IEEE FP.

Марк Глиссе комментирует:

Для встроенных типов gcc меняет местами операнды + без каких-либо особых мер предосторожности.


  • Конечные входы с ненулевыми результатами - это простой случай, очевидно, коммутативный. Сложение является одной из «базовых» математических операций FP, поэтому IEEE754 требует, чтобы результат был «правильно округлен» (ошибка округления <= 0,5 ulp), поэтому существует только один возможный числовой результат и только один битовый шаблон, который его представляет.

    Математика, не соответствующая IEEE FP, может допускать большие ошибки округления (например, допускать отклонение на единицу в младшем бите мантиссы, поэтому ошибка округления <= 1 ulp). Вероятно, он может быть некоммутативным, и конечный результат будет зависеть от того, какой операнд есть какой. Я думаю, что большинство людей сочли бы это плохим дизайном, но C++, вероятно, не запрещает этого.

  • Если результат равен нулю (конечные входные данные с одинаковыми величинами, но противоположными знаками), это всегда математика IEEE. (Или в режиме округления roundTowardNegative). Это правило распространяется на случай+0 + (-0.0)и наоборот, оба производят+0.0. См. Что такое (+0)+(-0) по стандарту IEEE с плавающей запятой?

    Входные данные с разными величинами не могут опускаться до нуля, если только у вас нет субнормальных входных данных для FPU, работающего в режиме сбрасывания в ноль (субнормальные выходные данные округляются до нуля). В таком случае вы можете получить-0.0в результате, если точный результат был отрицательным. Но он все еще коммутативен.

  • Сложение может дать -0.0 от-0 + -0, что тривиально коммутативно, потому что оба входа имеют одно и то же значение.

  • -Inf + все конечное есть -Inf. +Inf + все конечное есть +Inf. +Inf + -Inf — это NaN. Ни то, ни другое не зависит от порядка.

  • NaN + что-нибудь или что-нибудь + NaN — это NaN. «полезная нагрузка» (мантисса) NaN зависит от FPU. IIRC, сохраняя полезную нагрузку предыдущего NaN.

  • NaN + NaN дает NaN. Насколько я помню, ничто не указывает, какая полезная нагрузка NaN сохраняется или можно ли изобрести новую полезную нагрузку. Вряд ли кто-то что-то делает с полезной нагрузкой NaN, чтобы отслеживать, откуда она взялась, так что это не имеет большого значения.

Оба входа в C++ будут повышены до соответствующих типов. В частности, к более широкому из двух типов ввода, если они еще не совпадают. Так что асимметрии типов нет.


Дляa+b == b+aсамо по себе это может быть ложным для NaN из-за IEEE==семантика (не из-за+семантика), то же, чтоa+b == a+b.

Со строгой математикой FP (без дополнительной точности между операторами C, напримерgcc -ffloat-storeпри использовании устаревшей математики x87 на x86), я думаю, что равенство эквивалентно!isunordered(a,b)который проверяет, является ли любой из них NaN.

В противном случае возможно, что компилятор сможет выполнить , но не для другого, и вычислить один из них с более точными значениямиCSE с более ранним кодом для одногоaиb. (Строгий ISO C++ требует, чтобы высокоточные временные параметры существовали только внутри выражений даже для FLT_EVAL_METHOD==2 (например, x87) , а не между операторами, ноgccпо умолчанию не уважает это. Только сg++ -std=c++03или что там вместоgnu++20или с-ffloat-storeспециально для x87.)

В реализации C++ сFLT_EVAL_METHOD == 0(без дополнительной точности для временных значений в выражении), этот источник различий в оптимизации не будет фактором.

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