Если два языка следуют 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 …..

Значение греха (х) с использованием Scilab & C ++ = 0.12345ггггг …..

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


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

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

10

Решение

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

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

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

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

6

Другие решения

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

6

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

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

 x_8th = x*x*x*x*x*x*x*x;

Один из способов вычислить это — выполнить 7 умножений. Это будет поведение по умолчанию для большинства компиляторов. Тем не менее, вы можете ускорить это, указав опцию компилятора -ffastmath и полученный код будет иметь только 3 умножения:

temp1 = x*x; temp2 = temp1*temp1; x_8th = temp2*temp2;

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

3

Обратите внимание, что возможно, что 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
}
1
По вопросам рекламы [email protected]