Что на самом деле делает fcc-math gcc?
Я понимаю, --ffast-math
флаг gcc --ffast-math
может значительно увеличить скорость для операций с плавающей запятой и выходит за frameworks стандартов IEEE, но я не могу найти информацию о том, что на самом деле происходит, когда оно включено. Может кто-нибудь объяснить некоторые детали и, возможно, дать ясный пример того, как что-то изменилось бы, если флаг был включен или выключен?
Я попытался прорваться через SO для подобных вопросов, но не смог найти ничего, объясняющего работу ffast-math.
- Усечение Десятичное число не округлено
- Что означают F и D в конце числовых литералов?
- Почему 24.0000 не равно 24.0000 в MATLAB?
- Типы с плавающей запятой с фиксированным размером
- Когда следует использовать ключевое слово "strictfp" в java?
- Как преобразовать целое число в float в Java?
- Эмуляция «double» с использованием 2 "float" s
- Когда следует использовать double вместо десятичного?
- Как преобразовать строку в float?
- Построение 32-битного поплавка из 4 составных байтов
- Является ли добавление и умножение с плавающей запятой ассоциативным?
- Каков самый быстрый способ конвертировать float в int на x86
- Функции сравнения с плавающей запятой для C #
Как вы упомянули, это позволяет оптимизировать, не сохраняя строгого соответствия IEEE.
Примером может служить следующее:
x = x*x*x*x*x*x*x*x;
в
x *= x; x *= x; x *= x;
Поскольку арифметика с плавающей запятой не является ассоциативной, упорядочение и факторинг операций повлияют на результаты за счет округления. Поэтому эта оптимизация не выполняется при строгом режиме FP.
Я на самом деле не проверял, действительно ли GCC делает эту конкретную оптимизацию. Но идея такая же.
-ffast-math
делает намного больше, чем просто нарушает строгое соответствие IEEE.
Прежде всего, конечно, это нарушает строгое соответствие IEEE, позволяя, например, переупорядочить инструкции, что математически одно и то же (в идеале), но не то же самое в плавающей точке.
Во-вторых, он отключает установку errno
после математических команд с одной инструкцией, что означает отказ от записи в локальную переменную streamа (это может сделать 100% -ную разницу для этих функций на некоторых архитектурах).
В-третьих, он делает предположение, что всякая математика конечна , а это означает, что никаких проверок для NaN (или нуля) не производится, где они будут иметь пагубные последствия. Просто предполагается, что этого не произойдет.
В-четвертых, это позволяет взаимные приближения для деления и обратного квадратного корня.
Кроме того, он отключает подписанный ноль (код предполагает, что подписанный ноль не существует, даже если целевой его поддерживает) и математика округления, которая позволяет, среди прочего, постоянно складывать во время компиляции.
Наконец, он генерирует код, который предполагает, что аппаратные прерывания не могут произойти из-за математики сигнализации / захвата (т. Е. Если они не могут быть отключены в целевой архитектуре и, следовательно, они не будут обработаны).