Почему этот же код выдает два разных результата fp на разных машинах? - PullRequest
0 голосов
/ 14 февраля 2019

Вот код:

#include <iostream>
#include <math.h>

const double ln2per12 = log(2.0) / 12.0;

int main() {
    std::cout.precision(100);
    double target = 9.800000000000000710542735760100185871124267578125;
    double unnormalizatedValue = 9.79999999999063220457173883914947509765625;
    double ln2per12edValue = unnormalizatedValue * ln2per12;
    double errorLn2per12 = fabs(target - ln2per12edValue / ln2per12);
    std::cout << unnormalizatedValue << std::endl;
    std::cout << ln2per12 << std::endl;
    std::cout << errorLn2per12 << " <<<<< its different" << std::endl;
}

Если я попробую на своей машине (MSVC) или здесь (GCC):

errorLn2per12 = 9.3702823278363212011754512786865234375e-12

Вместо этого здесь (GCC):

errorLn2per12 = 9.368505970996920950710773468017578125e-12

, что отличается.Это из-за Машина Эпсилон ?Или флаги точности компилятора?Или другая IEEE оценка?

В чем причина этого смещения?Проблема возникает в функции fabs() (поскольку другие значения выглядят одинаково).

1 Ответ

0 голосов
/ 14 февраля 2019

Даже без -Ofast стандарт C ++ не требует, чтобы реализации были точными с log (или sin, или exp и т. Д.), Только чтобы они находились в пределах нескольких ульп (т.е. могутбудут некоторые неточности в последних двоичных местах).Это позволяет ускорить аппаратное (или программное) приближение, которое каждая платформа / компилятор может выполнять по-разному.

() Единственная математическая функция с плавающей запятой, с которой вы всегда получите идеальные результаты на всех платформах, - это sqrt. )

Более досадно, что вы даже можете получить разные результаты между компиляцией (компилятор может использовать некоторую внутреннюю библиотеку, чтобы быть настолько точной, как float / double допускает постоянные выражения) и временем выполнения (например, аппаратные аппроксимации).

Если вы хотите, чтобы log давал одинаковый результат на разных платформах и компиляторах, вам придется реализовать его самостоятельно, используя только +, -, *, / и sqrt (или найдите библиотеку с этой гарантией).И избегайте целого ряда ловушек на этом пути.

Если вам нужен детерминизм с плавающей запятой в целом, я настоятельно рекомендую прочитать эту статью, чтобы понять, насколько серьезна проблема у вас впереди: https://randomascii.wordpress.com/2013/07/16/floating-point-determinism/

...