c ++比较两个浮点值

时间:2011-02-21 09:44:33

标签: c++ floating-point

我想知道在这两种方式之间比较两个双倍有什么区别:

double a1 = ...;
double a2 = ....;
  1. fabs(a1-a2)<小量
  2. (fabs(a1-a2)/ a2)<小量
  3. 有没有更好的方法呢?

    感谢

5 个答案:

答案 0 :(得分:13)

我认为,

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

答案 1 :(得分:3)

就我个人而言,我使用std::nextafter来比较两个double。这会对最小epsilon的值(或factor)使用最小epsilon

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;
}

答案 2 :(得分:2)

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

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

答案 3 :(得分:2)

这已经得到了很好的解决,但有几点是有道理的:

fabs(a1-a2) < epsilon

a1a2之间的绝对差异与容差epsilon进行比较。如果您知道缩放先验(例如,如果a2实际上是常量),这可能是合适的,但如果您不知道有多大{{1},通常应该避免}和a1是。

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

a2

(注意除法是里面的绝对值;否则,这个条件对于负fabs((a1-a2)/a2) < epsilon )是无用的。对于大多数用途,相对误差更正确,因为它更接近于浮点舍入实际发生的方式,但是在某些情况下它不起作用并且您需要使用绝对容差(通常这是因为灾难性的取消)。您有时也会看到以这种形式写的相对错误界限:

a2

这通常会更有效率,因为它避免了分裂。

答案 4 :(得分:2)

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

fabs(a1-a2) < epsilon

这并不完美。如果a1a2是来自小数字操作的结果,则epsilon会很小。如果a1a2是来自大数字操作的结果,则epsilon会很大。

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

由于您希望按epsilon缩放a2,因此效果会更好一些。但是,如果0等于a2,则会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)))));
}