Значения с плавающей запятой ведут себя по-разному в сборках выпуска и отладки

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

Считаете ли вы это "различие" ошибкой или такого рода различий можно было бы ожидать. Будет ли это ошибка компилятора или ошибка внутренней библиотеки.

Например:

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

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

Проблема в компиляторе X360.

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
7
0
2 377
9
Перейти к ответу Данный вопрос помечен как решенный

Ответы 9

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

Ответ принят как подходящий

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

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

Взгляните на / fp (указать поведение с плавающей запятой).

Не ошибка. Такой разницы следовало ожидать.

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

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

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

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

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

Не знаю, на X360 такое тоже происходит или нет.

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

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

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

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

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

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