Что делает gcc ffast-math на самом деле?

Я понимаю, что флаг gcc --ffast-math может значительно увеличить скорость для операций с плавающей запятой и выходит за рамки стандартов IEEE, но я не могу найти информацию о том, что происходит на самом деле. Может кто-нибудь объяснить некоторые детали и, возможно, дать ясный пример того, как что-то изменится, если флаг включен или выключен?

Я попытался прорваться через S.O. для подобных вопросов, но не смог найти ничего, объясняющего работу ffast-math.

Ответ 1

Как вы упомянули, он позволяет оптимизировать, не сохраняя строгого соответствия IEEE.

Пример:

x = x*x*x*x*x*x*x*x;

к

x *= x;
x *= x;
x *= x;

Поскольку арифметика с плавающей запятой не является ассоциативной, упорядочение и факторинг операций повлияют на результаты за счет округления. Поэтому эта оптимизация не выполняется при строгом FP-поведении.

EDIT: на самом деле я не проверял, действительно ли GCC делает эту конкретную оптимизацию. Но идея такая же.

Ответ 2

-ffast-math делает намного больше, чем просто нарушает строгое соответствие IEEE.

Прежде всего, конечно, это нарушает строгое соответствие IEEE, разрешая, например, переупорядочение инструкций для чего-то математически одного и того же (в идеале), но не то же самое в плавающей точке.

Во-вторых, он отключает настройку errno после математических команд с одной инструкцией, что означает отказ от записи в локальную переменную потока (это может сделать 100% -ную разницу для этих функций на некоторых архитектурах).

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

В-четвертых, это позволяет взаимные приближения для деления и обратного квадратного корня.

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

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