Могу ли я гарантировать, что компилятор C++ не изменит порядок моих вычислений?

в настоящее время я читаю отличные библиотека для Double-Double и Quad-Double арифметики бумага, и в первых нескольких строках я замечаю, что они выполняют сумму следующим образом:

std::pair<double, double> TwoSum(double a, double b)
{
    double s = a + b;
    double v = s - a;
    double e = (a - (s - v)) + (b - v);
    return std::make_pair(s, e);
}

вычисление ошибки,e, полагается на то, что расчет следует этому порядку операций именно из-за неассоциативных свойств математики с плавающей запятой IEEE-754.

если я скомпилирую это в рамках современного оптимизация компилятора C++ (например, MSVC или gcc), можно ли гарантировать, что компилятор не оптимизирует способ выполнения этого вычисления?

во-вторых, гарантируется ли это в любом месте стандарта C++?

10 ответов


Да, это безопасно (по крайней мере в данном случае). Вы используете только два "оператора", основное выражение


вы можете посмотреть страницу руководства g++: http://gcc.gnu.org/onlinedocs/gcc-4.6.1/gcc/Optimize-Options.html#Optimize-Options

особенно-fassociative-math,-ffast-math и-ffloat-store

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


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

см http://software.intel.com/sites/products/documentation/hpc/compilerpro/en-us/cpp/lin/compiler_c/copts/common_options/option_fp_model.htm#option_fp_model


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

но будьте осторожны с расширенной точностью регистров FP.

обратитесь к документации компилятора о том, как убедиться, что значения FP не имеют расширенной точности.


В общем, вы должны быть в состоянии -- оптимизатор должен знать свойства реальных операций.

тем не менее, я бы проверил компилятор, который я использовал.


да. Компилятор не будет изменять порядок ваших вычислений в таком блоке.


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

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

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


Если вам действительно нужно, я думаю, вы можете сделать функцию noinline no_reorder(float x) { return x;}, а затем использовать ее вместо скобок. Очевидно, что это не особенно эффективное решение.


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

различные уровни оптимизации (в gcc, -O _O2 и т. д.) позволяют перестроить код (однако такой последовательный код вряд ли будет затронут) , но я бы предложил вам изолировать эту конкретную часть код в отдельный файл, так что вы можете управлять уровнем оптимизации для расчета.


короткий ответ: компилятор, вероятно, изменит порядок ваших вычислений, но он никогда не изменит поведение вашей программы (если ваш код не использует выражение с неопределенным поведением:http://blog.regehr.org/archives/213)

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

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