【问题标题】:Double comparison (again)双重比较(再次)
【发布时间】:2014-09-26 20:15:00
【问题描述】:

我有一个完全准确的“黄金来源”算法,即输出精确到双精度。

我想比较一下另一种算法,判断它是否也能精确到双精度。

例如,可能会输出以下三个数字:

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

A 是黄金来源。我可以看到 A 和 X 等于 15 个有效数字,而 Y 在第 13 个有效数字上不同。因此,Y 不等于其他双精度,而 A 和 X 可能等于双精度。

我看到了this question,但我不确定如何应用它。如果我使用 n=1,它表示 X 相等,而 Y 不相等,这似乎是合理的,但它是否正确?我希望它适用于 n=0,但这表明 X 和 Y 不相等(也许这是正确的)。

【问题讨论】:

  • @πάντα ῥεῖ,我认为这无关。但是,我链接到的问题确实允许我判断数字是否等于 n 有效数字,所以我可以设置 n=15 - 也许这是可以做到的最好的?我想部分问题是:精确到双精度意味着什么?
  • “精确到双精度是什么意思?”嗯,你实际上需要决定哪个特定的精度应该用于比较关于平等。我承认,我提供的链接只是间接相关。我将它用作another question 的副本,它更准确地询问如何将floatdouble 限制为指定的精度和舍入行为。
  • 另一个关于这个主题的紧急推荐阅读:What Every Computer Scientist Should Know About Floating-Point Arithmetic
  • 设置 n=15 不起作用,因为我们处于双精度的边缘。例如,0.138034776379449 和 0.13803477637944886 被报告为在 15 sig 时不同。无花果。我已经尝试阅读链接的文档,但会再试一次,谢谢。

标签: c++ double-precision


【解决方案1】:

双精度数在内部以尾数和指数形式存储。平等检查双精度浮点数是没有用的,因为它们可能非常非常接近,甚至不完全相同。因此,您需要定义一个阈值。例如,您将 epsilon 定义为 0.000000001 左右(精度取决于您的精度容差)。然后,如果 a, b 是两个双精度数,检查 abs(a-b)

【讨论】:

  • 绝对比较不起作用。在我的例子中,std::abs(X-A)=8.1e-20,std::abs(Y-A)=1.8e-17,两者都小于std::numeric_limits::epsilon()。
  • @Rai 对,这就是为什么您需要根据比较和精度的要求使用 epsilon
  • @Rai 例如,您可以根据需要定义 epsilon=1e-19 或更高或更低的精度
  • 我认为您建议根据我要比较的数字的大小来改变 epsilon。我应该如何确定正确的 epsilon?例如,我可能正在比较大约等于 1 的数字,在这种情况下 1e-19 是不合适的。或者数字可能是 1e-250 的顺序。
  • @Rai 是 epsilon 的值,其精度将根据您的要求而有所不同。如果你认为 1.004 和 1.003 是等价的,那么 0.0005 的 epsilon 值就足够了
【解决方案2】:

我真的认为关键问题是:精确到双精度意味着什么?

我认为有一个简单的答案:如果r 是真正的答案,而d 是双重表示,那么d-r<=d'-r 对于所有其他可能的双重表示,d'

这意味着我的 2nd 算法只有在其输出等于黄金源的输出或真正的解决方案恰好位于算法输出之间的一半时才能精确到双精度。


我的 2nd 算法不是那么准确。

现在问题演变为:我需要什么精度?我在与 Debasish 的交流中确定绝对差异不是一个合适的度量标准。我怀疑相对差异更好,但不是我想要的。

我选择了以下算法来报告“差异”,它将 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)。我不确定相对差异表明什么(如果有的话)。

应该有更好的方法来做到这一点,而无需字符串...

【讨论】:

    猜你喜欢
    • 2012-10-09
    • 2011-08-02
    • 2019-01-10
    • 1970-01-01
    • 1970-01-01
    • 2017-07-08
    • 1970-01-01
    • 1970-01-01
    • 2022-08-02
    相关资源
    最近更新 更多