【问题标题】:c++ comparing two floating point valuesc ++比较两个浮点值
【发布时间】:2016-02-21 22:28:16
【问题描述】:

我想知道这两种方式比较两个双精度有什么区别:

double a1 = ...;
double a2 = ....;
  1. 晶圆厂(a1-a2)
  2. (晶圆厂(a1-a2)/a2)

有没有更喜欢的方式来做到这一点?

谢谢

【问题讨论】:

  • “首选方式”在很大程度上取决于周围的算法。两者都有其用途(还有更冗长的fabs(a1 - a2) / min(a1, a2)fabs(a1 - a2) / max(a1, a2),尤其是max(fabs(a1 - a2), fabs(a1 - a2) / a1),当你不知道数字是否接近于零时)。

标签: c++ floating-point


【解决方案1】:

This article 非常彻底地回答了你的问题,我想。您可能想跳到“Epsilon 比较”部分。

【讨论】:

  • 添加了链接文章的引用,希望您不介意,如果您愿意,请随时回滚。
  • 我担心那句话并没有真正回答他的问题:我认为他知道精度问题,只是问为什么要除以 a2。我原本考虑引用一些东西,但我认为这篇文章最好保持原样。我已回滚并编辑以指示最相关的部分。
【解决方案2】:

就我个人而言,我使用std::nextafter 来比较两个double。这在一个值上使用最小的epsilon(或最小的epsilonfactor)。

bool nearly_equal(double a, double b)
{
  return std::nextafter(a, std::numeric_limits<double>::lowest()) <= b
    && std::nextafter(a, std::numeric_limits<double>::max()) >= b;
}

bool nearly_equal(double a, double b, int factor /* a factor of epsilon */)
{
  double min_a = a - (a - std::nextafter(a, std::numeric_limits<double>::lowest())) * factor;
  double max_a = a + (std::nextafter(a, std::numeric_limits<double>::max()) - a) * factor;

  return min_a <= b && max_a >= b;
}

【讨论】:

  • nearly_equal(..,..,epsilon) 的结果不等于:fabs(right-left) &lt; epsilon ?
  • nearly_equal(a, b, factor)factor * epsilon(a) 的容差检查相等性。 fabs(a-b) &lt; epsilon 检查与给定epsilon 容差的相等性。这两种表达方式都很好,可以根据我们想要测试的内容使用。有时,我们想检查一个固定的 epsilon(即使很难找到正确的 epsilon:epsilon (1e20) = 16384)。有时,我们想用 epsilon 的因子进行检查(即使也很难找到正确的因子)。现在对于fabs(a-b) &lt; fabs(a) * epsilon,使用nearly_equal(a, b, epsilon) 会更准确。
【解决方案3】:

Epsilon 根据双倍范围内的值而变化。如果您想使用您的自己的固定 epsilon,我建议为1 选择一个epsilon。

fabs(a1-a2) < epsilon

这并不完美。如果a1a2 是小数运算的结果,则ε 会很小。如果 a1a2 是大数字运算的结果,那么 epsilon 会很大。

fabs((a1-a2)/a2) < epsilon

这会更好一些,因为您希望将 epsilon 缩放到 a2。但是,如果 a2 等于 0,则除以 0

fabs(a1-a2) < fabs(a2)*epsilon

这有点好。但是,如果 a2 等于 0,则这是不正确的。 fabs(a2)*epsilon 将等于 0 并且两个值 a1=0.000000001a2=0 的比较将始终失败。

fabs(a1-a2) < max(fabs(a1), fabs(a2))*epsilon

这有点好。但是,这是不正确的,因为epsilon连续 方式中不成比例。使用 IEEE 编码,epsilon 与基于 2离散 方式的值成比例。

fabs(a1-a2) < 2^((int)log2(max(fabs(a1), fabs(a2)))*epsilon

a1a2 不等于0 时,这对我来说是正确的。

泛型方法的实现可以是:

bool nearly_equal(double a1, double a2, double epsilon)
{
  if (a1 == 0 && a2 == 0)
    return true;

  return std::abs(a1 - a2) < epsilon * pow (2.0, static_cast<int> (std::log2(std::max(std::abs(a1), std::abs(a2)))));
}

【讨论】:

    【解决方案4】:

    前者只比较绝对值,而后者比较相对值。假设 epsilon 设置为 0.1:如果 a1a2 较大,这可能就足够了。但是,如果两个值都接近于零,则第一种方法将认为大多数值相等。

    这实际上取决于您处理的价值观类型。如果您使用在数学上更合理的第二种情况,请务必考虑a2==0 的情况。

    【讨论】:

      【解决方案5】:

      这已经很好地解决了,但有几点需要说明:

      fabs(a1-a2) < epsilon
      

      正在将a1a2 之间的绝对差异与容差epsilon 进行比较。如果您知道缩放 先验(例如,如果 a2 实际上是一个常数),这可能是合适的,但如果您不知道 a1 和 @ 有多大,通常应该避免使用987654327@是。

      您的第二个选项几乎可以计算相对差异,但有一个错误;它实际上应该是:

      fabs((a1-a2)/a2) < epsilon
      

      (注意除法是里面的绝对值;否则,这个条件对于负数a2是没有用的)。相对误差对于大多数用途来说更正确,因为它更接近地反映了浮点舍入实际发生的方式,但在某些情况下它不起作用并且您需要使用绝对容差(通常这是因为 灾难性取消)。您有时还会看到以这种形式编写的相对误差范围:

      fabs(a1-a2) < fabs(a2)*epsilon
      

      这通常更有效,因为它避免了除法。

      【讨论】:

      • 真的很喜欢最后一种形式,不太明白什么情况下不行
      • 如果我的号码是 10e-7 到 10e6,我应该使用哪个 epsilon 值?
      • @Guillaume07:如果值分布在这么大的范围内,您当然希望使用相对误差比较之一(我列出的第二个或第三个选项)。更微妙的问题是“如何选择合适的 epsilon 值?”
      • 请注意,在进行相对差异 epsilon 比较时,您不应盲目地除以 a2(或 a1)。如果你这样做,那么你会得到不对称,其中 compare(a,b) 给出了与 compare(b,a) 不同的结果。那会很糟糕。您需要始终除以具有最大或最小绝对值的数字。像这样:自动最大 = max(fabs(a1),fabs(a2)); return fabs(a1-a2)
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-08-24
      • 2017-06-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多