Если два языка следуют IEEE 754, будут ли вычисления на обоих языках приводить к одинаковым ответам?


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

Проблема в том, что каждый шаг цикла использует вычисления из предыдущего шага. Кроме того, разница между вычислениями становится очевидной только около 100 000-й итерации (из примерно 300 000).

Примечание: я сравниваю выходные данные моей программы C++ с выходными данными Scilab 5.5.2, используя команду" format(25);". То есть я сравниваю 25 значащих цифр. Я также хотел бы отметить, что я понимаю, как точность не может быть гарантирована после определенного количества битов, но прочитайте разделы ниже, прежде чем комментировать. До сих пор все вычисления были идентичны до 25 цифр между двумя языками.

В попытках добраться до сути этого вопрос, до сих пор я пытался:

  1. анализ используемого типа данных:

Мне удалось подтвердить, что Scilab использует двойники IEEE 754 (согласно языковой документации). Кроме того, согласно Википедии, C++ не требуется для использования IEEE 754 для двойников, но из того, что я могу сказать, везде, где я использую двойник в C++, он идеально соответствует результатам Scilab.

  1. изучение использования трансцендентного функции:

Я также прочитал из , что каждый компьютерный ученый должен знать об арифметике с плавающей запятой, что IEEE не требует точного округления трансцендентных функций. Имея это в виду, я сравнил результаты этих функций (sin (), cos (), exp ()) в обоих языках и снова результаты оказались одинаковыми (до 25 цифр).

  1. использование других функций и предопределенных значений:

Я повторил выше приведены шаги для использования sqrt () и pow (). А также Значение Pi (я использую M_PI в C++ и %pi в Scilab). И снова результат был тот же.

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

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

Значение sin (x) при использовании Wolfram Alpha = 0.123456789.....

Значение sin (x) с помощью Scilab & C++ = 0.12345 yyyyy.....

где даже однажды значение, вычисленное с помощью Scilab или C++, начало отличаться от фактического результата (от Wolfram). Результаты каждого языка все еще соответствовали друг другу. Это приводит меня к мысли, что большинство значений вычисляются (между двумя языками) в одном и том же путь. Даже несмотря на то, что IEEE 754 этого не требует.


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

Возможно ли, что даже при том, что все входы в эти циклы идентичны, результаты могут быть разными? Возможно, потому, что происходит очень маленькая ошибка (мимо того, что я могу видеть с 25 цифрами), которая накапливается в течение время? Если да, то как я могу решить эту проблему?

4 10

4 ответа:

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

Функции

, такие как sin(x), могут быть реализованы различными способами, используя один и тот же язык (а также разные языки). Отличный пример-функция sin(x). Многие реализации будут использовать таблицу поиска или таблицу поиска с интерполяцией. Это имеет преимущества скорости. Однако некоторые реализации могут использовать ряд Тейлора для оценки функции. Некоторые реализации могут использовать многочлены, чтобы получить близкое приближение.

Наличие одного и того же числового формата является одним из препятствий для решения между языками. Другое дело-реализация функций.

Помните, что вам также нужно рассмотреть платформу. Программа, использующая 80-битный процессор с плавающей запятой, будет иметь другие результаты, чем программа, использующая 64-битную программную реализацию с плавающей запятой.

Некоторые архитектуры предоставляют возможность использования расширенных прецизионных регистров с плавающей запятой (например, 80 бит внутри, по сравнению с 64-битными значениями в ОЗУ). Таким образом, можно получить несколько разные результаты для одного и того же вычисления, в зависимости от структуры вычислений и уровня оптимизации, используемого для компиляции кода.

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

Например, предположим, что у вас есть этот код:

 x_8th = x*x*x*x*x*x*x*x;
Один из способов вычислить это-выполнить 7 умножения. Это будет поведение по умолчанию для большинства компиляторов. Однако вы можете ускорить это, указав параметр компилятора -ffastmath , и полученный код будет иметь только 3 умножения:
temp1 = x*x; temp2 = temp1*temp1; x_8th = temp2*temp2;
Результат будет немного отличаться, потому что арифметика конечной точности не ассоциативна, но достаточно близка для большинства приложений и намного быстрее. Однако, если ваши вычисления не являются хорошо обусловленная эта маленькая ошибка может быстро усилиться в большую.

Обратите внимание, что возможно, что Scilab и C++ не используют одну и ту же последовательность команд, или что один использует FPU, а другой-SSE, поэтому не может быть способа заставить их быть точно такими же.

Как прокомментировал IInspectable, если ваш компилятор имеет _control87() или что-то подобное, вы можете использовать его для изменения параметров точности и/или округления. Вы можете попробовать комбинации этого, чтобы увидеть, если это имеет какой-либо эффект, но опять же, даже вам удается получить идентичные настройки для Scilab и C++ проблема может заключаться в различиях в фактических последовательностях команд.

Http://msdn.microsoft.com/en-us/library/e9b52ceh.aspx

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

В случае использования FPU в 32-битном режиме, и если ваш компилятор не имеет что-то вроде _control87, вы можете использовать ассемблерный код. Если встроенная сборка не разрешена, необходимо вызвать функцию сборки. Этот пример взят из старой тестовой программы:

static short fcw; /* 16 bit floating point control word */
/* ... */
/* set precision control to extended precision */
__asm{
    fnstcw fcw
    or fcw,0300h
    fldcw fcw
}