Параметры компилятора с плавающей точкой C ++ | Предотвращение A / B -> A * (1 / B)
-
25-09-2019 - |
Вопрос
Я пишу числовое программное обеспечение в Realtime, в C ++, в настоящее время составляющим его с Visual-C ++ 2008. Теперь используя «быструю» модель с плавающей точкой (/fp:fast
), Различные оптимизации, большинство из них полезны мое дело, но конкретно:
a/b -> a*(1/b) Division by multiplicative inverse
слишком численно нестабилен для многих расчетов.
(видеть: Microsoft Visual C ++ оптимизация с плавающей запятой)
Переключение /fp:precise
Заставляет мое приложение работать более чем в два раза ниже. Можно ли либо точно настроить оптимизатор (т. Е. Отключить эту специфическую оптимизацию), либо как-то вручную обходить его?
- Фактический пример минимального кода: -
void test(float a, float b, float c,
float &ret0, float &ret1) {
ret0 = b/a;
ret1 = c/a;
}
мой фактический код - это в основном алгоритмы, связанные с матрицей
Выход: VC (CL, версия 15, 0x86):
divss xmm0,xmm1
mulss xmm2,xmm0
mulss xmm1,xmm0
Наличие One Div, вместо двух, численно является большой проблемой, (XMM0, предварительно загружен с 1,0f из оперативной памяти), как в зависимости от значений XMM1,2 (что может быть в разных диапазонах), вы можете потерять много точности ( Компиляция без SSE, выводит аналогичный код стека-X87-FPU).
Упаковка функции с
#pragma float_control( precise, on, push )
...
#pragma float_control(pop)
Решает проблему точности, но во-первых, она доступна только на уровне функциональности (глобально-охвата), а во-вторых, он предотвращает встроенную функцию, (т. Е. Наказание на скорости слишком высоки)
«Точный» вывод отличается на «двойной» взад и вперед, а также хорошо:
divsd xmm1,xmm2
cvtsd2ss xmm1,xmm1
divsd xmm1,xmm0
cvtpd2ps xmm0,xmm1
Решение 5
(Странное) решение, которое я нашел: всякий раз, когда разделяться на то же значение в функции - добавьте несколько эпсилона:
a/b; c/b
->
a/(b+esp1); c/(b+esp2)
Также спасает вас от случайного div на ноль
Другие советы
Добавить
#pragma float_control( precise, on)
до вычисления и
#pragma float_control( precise,off)
после этого. Я думаю, что это должно сделать это.
В этом документе говорится, что вы можете управлять оптимизацией по поплавлению поплавковых настроек на линейной основе с помощью прагмы.
Есть также __assume. Вы можете использовать __ansume (A / B! = (A * (1 / b))). Я никогда не использовал __ansume, но в теории он существует точно, чтобы точно настроить оптимизатор.
Можете ли вы поставить функции, содержащие эти расчеты в отдельном файле исходного кода и компилируйте только этот файл с различными настройками?
Я не знаю, если это безопасно, если вам нужно будет проверить!