Значения Float ведут себя по-разному в сборках release и debug

мое приложение генерирует различные значения с плавающей запятой, когда я компилирую его в режиме выпуска и в режиме отладки. Единственная причина, по которой я узнал, заключается в том, что я сохраняю двоичный журнал трассировки, а тот из сборки выпуска немного отличается от сборки отладки, похоже, что нижние два бита 32-битных значений float отличаются примерно на 1/2 случаев.

считаете ли вы эту "разницу" ошибкой или ожидаете, что этот тип разницы. Это ошибка компилятора или ошибка внутренней библиотеки.

например:

LEFTPOS and SPACING are defined floating point values.
float def_x;
int xpos;

def_x = LEFTPOS + (xpos * (SPACING / 2));

проблема связана с компилятором X360.

9 ответов


режим выпуска может иметь другой набор стратегий FP. Существуют различные арифметические режимы с плавающей запятой в зависимости от уровня оптимизации, который вы хотите. MSVC, например, имеет строгие, быстрые и точные режимы.


Я знаю, что на ПК регистры с плавающей запятой имеют ширину 80 бит. Поэтому, если расчет выполняется полностью в пределах FPU, вы получаете преимущество в 80 бит точности. С другой стороны, если промежуточный результат перемещается в обычный регистр и обратно, он усекается до 32 бит, что дает разные результаты.

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

Я не знаю, происходит ли это на x360 тоже или нет.


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

посмотри /fp (укажите поведение с плавающей запятой).


Это не ошибка. Любой uperation с плавающей точкой имеет некоторые неточности. В режиме выпуска оптимизация изменит порядок операций, и вы получите немного другой результат. Разница должна быть небольшой. Если он большой у вас могут быть другие проблемы.


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


не ошибка. Такого рода различий можно ожидать.

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


это несоответствие может быть вызвано оптимизацией компилятора, которая обычно выполняется в режиме выпуска, но не в режиме отладки. Например, компилятор может переупорядочить некоторые операции для ускорения выполнения, что может привести к небольшой разнице в результате с плавающей запятой.

Итак, я бы сказал, скорее всего, это не ошибка. Если вы действительно беспокоитесь об этом, попробуйте включить оптимизацию в режиме отладки.


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

Если вам нужны стабильные результаты, можно сделать переменным, нестабильным, что приведет к более медленным, менее точным, но стабильные результаты.


Если вы установили переключатель компилятора, который позволил компилятору переупорядочить операции с плавающей запятой, -- например, /fp: fast -- то, очевидно, это не ошибка.

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