Как хранить двойники в памяти - PullRequest
0 голосов
/ 22 февраля 2011

Недавно я изменил какой-то код

double d0, d1;
// ... assign things to d0/d1 ...
double result = f(d0, d1)

до

double d[2];
// ... assign things to d[0]/d[1]
double result = f(d[0], d[1]);

Я не изменил ни одно из назначений на d, ни вычисления в f, ни что-либо еще, кроме того факта, что числа теперь хранятся в массиве фиксированной длины.

Однако при компиляции в режиме выпуска с включенными оптимизациями result изменился.

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


РЕДАКТИРОВАТЬ: Я попытаюсь получить некоторый код, демонстрирующий проблему, однако это довольно сложно, поскольку процесс, через который проходят эти числа, огромен (много математики, числовых вычислителей и т. Д.).

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

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

Ответы [ 4 ]

2 голосов
/ 22 февраля 2011

Возможно, у вас включен переключатель компилятора 'fast math', или вы что-то делаете в функции "назначить вещи" (чего мы не видим), которая позволяет компилятору юридически переупорядочивать вычисления.Несмотря на то, что последовательности эквивалентны, вероятно, оптимизатор обрабатывает их по-разному, так что в итоге вы получите немного другую генерацию кода.Если он переупорядочен, вы получите небольшие различия в младших разрядах.Такова жизнь с плавающей запятой.

Вы можете предотвратить это, не используя «быструю математику» (если она включена) или форсируя упорядочение посредством построения формул и промежуточных значений.Даже это трудно (невозможно?) Гарантировать.Вопрос на самом деле «Почему компилятор генерирует другой код для массивов и пронумерованных переменных?», Но это в основном анализ генератора кода.

0 голосов
/ 22 февраля 2011

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

Вполне возможно, что double[2] приведет к тому, что массив будет выделен как два смежных стековых слота там, где его раньше не было, и это, в свою очередь, может привести к переупорядочению кода для повышения эффективности доступа к памяти. IEEE754 математика с плавающей запятой не подчиняется обычным математическим правилам, то есть a + b + c! = C + b + a

0 голосов
/ 22 февраля 2011

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

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

К вашим проблемам: выравнивание памяти не может повлиять на значение типа double, и компилятор должен иметь возможность генерировать эквивалентный код для любого примера,поэтому вам не нужно беспокоиться, что вы что-то делаете неправильно (по крайней мере, не в ограниченном примере, который вы опубликовали).

0 голосов
/ 22 февраля 2011

нет, это эквивалентно - у вас что-то не так.

Проверьте / fp: точные флажки (или эквивалентные) аппаратного обеспечения процессора с плавающей запятой, которое может работать в более точном или более скоростном режиме - в оптимизированной сборке оно может иметь другое значение по умолчанию

...