Двойное сравнение (снова)

У меня есть алгоритм "золотого источника", который является абсолютно точным, т.е. вывод точен до двойной точности.

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

Например, могут быть выведены следующие три числа:

A = 8.5534733167898555463e-05
X = 8.5534733167898640989e-05
Y = 8.553473316788089652e-05
sig:1 23456789012345

А — золотой источник. Я вижу, что A и X равны 15 значащим цифрам, а Y отличается 13-й значащей цифрой. Следовательно, Y не равно другим значениям двойной точности, в то время как A и X могут быть равными значениям двойной точности.

Я видел этот вопрос, но я точно не знаю, как его применить. Если я использую n=1, это сигнализирует, что X равно, а Y нет, что кажется разумным, но правильно ли это? Я надеялся, что это будет работать с n = 0, но это сигнализирует о том, что и X, и Y не равны (может быть, это правильно).


person Rai    schedule 25.09.2014    source источник
comment
@ πάντα ῥεῖ, я не думаю, что это связано. Однако вопрос, на который я ссылался, позволяет мне определить, равны ли числа n значащим цифрам, поэтому я могу установить n = 15 - может быть, это лучшее, что можно сделать? Я полагаю, что часть вопроса заключается в следующем: что значит быть точным с двойной точностью?   -  person Rai    schedule 25.09.2014
comment
Еще настоятельно рекомендуется прочитать об этой теме: Что каждый компьютер Ученый должен знать об арифметике с плавающей запятой.   -  person πάντα ῥεῖ    schedule 25.09.2014
comment
Установка n=15 не работает, так как мы находимся на грани двойной точности. Например, 0,138034776379449 и 0,13803477637944886 считаются разными при 15 знаках. инжир Я попытался прочитать связанный документ, но попробую еще раз, спасибо.   -  person Rai    schedule 25.09.2014


Ответы (2)


Числа двойной точности хранятся внутри в форме мантиссы и экспоненты. Проверка равенства в числах с плавающей запятой двойной точности бесполезна, так как они могут быть очень-очень близкими, даже не совсем одинаковыми. Итак, вам нужно определить порог. Например, вы определяете эпсилон как, скажем, 0,000000001 или около того (точность зависит от вашего допуска на точность). Затем, если a, b - два двойных числа, проверьте, принимает ли abs(a-b) ‹ эпсилон как равенство или почти равенство.

person Dr. Debasish Jana    schedule 25.09.2014
comment
Абсолютное сравнение не работает. В моем примере std::abs(X-A)=8.1e-20, std::abs(Y-A)=1.8e-17, оба меньше, чем std::numeric_limits‹double›::epsilon(). - person Rai; 25.09.2014
comment
@Rai правильно, вот почему вам нужно использовать эпсилон в соответствии с вашими требованиями к сравнению и точности. - person Dr. Debasish Jana; 25.09.2014
comment
@Rai, например, вы можете определить epsilon=1e-19 или более или менее точную точность по мере необходимости. - person Dr. Debasish Jana; 25.09.2014
comment
Я думаю, вы рекомендуете варьировать эпсилон в зависимости от размера чисел, которые я сравниваю. Как мне определить правильный эпсилон? Например, я могу сравнивать числа, примерно равные 1, и в этом случае 1e-19 будет неуместным. Или числа могут быть порядка 1e-250. - person Rai; 25.09.2014
comment
@Rai да, значение эпсилон и его точность будут варьироваться в зависимости от ваших требований. Если вы считаете, что 1,004 и 1,003 эквивалентны, то достаточно значения 0,0005 эпсилон. - person Dr. Debasish Jana; 25.09.2014
comment
Мое требование состоит в том, чтобы определить, равны ли результаты двух алгоритмов двойной точности. Я подозреваю, что на самом деле это может означать, совпадают ли два числа в первых 15 значащих цифрах?, но я надеюсь, что кто-нибудь сможет объяснить. - person Rai; 25.09.2014
comment
В качестве альтернативы вы можете умножить оба на 1e15, взять целую часть и сравнить как целые числа, сделано? - person Dr. Debasish Jana; 25.09.2014
comment
Не думайте, что это работает для примера, который я привел. Если вы умножите A и Y на 1e15 и возьмете целую часть, они будут равны. - person Rai; 25.09.2014

Я действительно думаю, что ключевой вопрос заключается в следующем: что значит быть точным до двойной точности?

И я думаю, что есть простой ответ: если r — настоящий ответ, а d — двойное представление, то d-r<=d'-r для всех других возможных двойников — d'.

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


Мой 2nd алгоритм не такой точный.

Теперь возникает вопрос: какая точность мне требуется? В обмене с Дебасишем я установил, что абсолютные различия не являются подходящей метрикой. Я подозреваю, что относительные различия лучше, но не совсем то, что я хочу.

Я остановился на следующем алгоритме, чтобы сообщить о «разнице», который считает равенство до 15 значащих цифр равенством:

double doubles_differ(double a, double b) {
  const int significant_figures = 15;

  // This test will catch most cases
  if (std::abs(a - b) < pow(.1, significant_figures)
                        * std::max(std::abs(a), std::abs(b)))
    return 0;

  // Because we are at the edge of double precision, sometimes a case that is
  // actually equal slips through the above test. The following should be more
  // robust, but a lot slower.
  std::stringstream ss_a;
  std::stringstream ss_b;
  ss_a << std::setprecision(significant_figures - 1) << std::scientific << a;
  ss_b << std::setprecision(significant_figures - 1) << std::scientific << b;
  std::string s_a = ss_a.str();
  std::string s_b = ss_b.str();
  if (s_a == s_b)
    return 0;

  // Finally, return the difference scaled to unity.
  // Format: "7.70612131004268e-013" = significand, followed by "e±nnn".
  if (s_a.substr(s_a.length() - 5, 5) != s_b.substr(s_b.length() - 5, 5))
    throw std::runtime_error("Big diff");

  std::stringstream ss_convert_a(s_a.substr(0, s_a.length() - 6));
  std::stringstream ss_convert_b(s_b.substr(0, s_b.length() - 6));
  double a_converted;
  double b_converted;
  if (!(ss_convert_a >> a_converted))
    throw std::runtime_error("Could not convert a");
  if (!(ss_convert_b >> b_converted))
    throw std::runtime_error("Could not convert b");

  return std::abs(a_converted - b_converted);
}

Наконец, некоторые примеры данных:

a     = 9.9399367132570751e-016
b     = 9.9399367132633209e-016
index   1 2345678901234567
diff  = 6.3007377093526884e-012
relative diff = 6.2835472672107563e-013

Обратите внимание, что показатель степени diff указывает количество значащих цифр точности (в данном случае 12). Я не уверен, на что (если вообще что-либо) указывает относительная разница.

Должен быть лучший способ сделать это без строк...

person Rai    schedule 26.09.2014