【问题标题】:Is to round a correct way for making float-double comparison是四舍五入进行浮点双比较的正确方法
【发布时间】:2014-01-30 16:18:45
【问题描述】:

以this问题为基础,众所周知,我们不应该对十进制变量应用equals比较操作,因为数字错误(它不受编程语言约束):

bool CompareDoubles1 (double A, double B)
{
   return A == B;
}

上面的代码是不对的。 我的问题是:

  1. 对这两个数字取整然后比较是否正确?
  2. 效率更高?

例如:

bool CompareDoubles1 (double A, double B)
    {
       double a = round(A,4);
       double b = round(B,4)
       return a == b;
    }

对吗?

编辑

我正在考虑 round 是一种采用 double(数字)和 int(精度)的方法:

bool round (float number, int precision);

编辑 我认为通过这种比较方法可以更好地理解我对这个问题的意思:

bool CompareDoubles1 (double A, double B, int precision)
        {
           //precition could be the error expected when rounding
           double a = round(A,precision);
           double b = round(B,precision)
           return a == b;
        }

【问题讨论】:

  • round的定义是什么?
  • 比较浮点数的正确方法是根本不比较。
  • 第一个代码是正确的,即使比较很少为真。如果您想要按位相等,您必须 使用第一个代码。使用“信任区域”或其他任何名称的关键在于,在大多数情况下,您的意思不是按位相等,而是“这两个数字在某种精度上是否相同?”
  • 我想说你需要知道的一切都在你引用的stackoverflow帖子中。无需重新开始讨论。
  • 如果您希望彼此接近的数字导致比较返回真,那么舍入不是方法,因为两个接近的数字可能会舍入为两个不同的数字,但其他数字彼此相距较远,可能会舍入相同。例如:1.0 和 1.4999 都可能舍入为 1,但 1.500001 可能舍入为 2

标签: c++ performance algorithm comparison


【解决方案1】:

通常,如果你真的需要比较浮点值,你会指定一个容差:

bool CompareDoubles1 (double A, double B, double tolerance)
{
   return std::abs(A - B) < tolerance;
}

选择适当的公差将取决于值的性质和产生它们的计算。

舍入不合适:您想要比较相等的两个非常接近的值可能会朝不同的方向舍入并且看起来不相等。例如,当舍入到最接近的整数时,0.3 和 0.4 会比较相等,但 0.499999 和 0.500001 不会。

【讨论】:

  • 或者您将公差设为相对。或者你设计你的算法,使相等的测试是合适的;很多情况下==可以在浮点上正确使用。
  • 我认为&lt;= 会比&lt; 更好。否则tolerance == 0 不起作用(模仿传统的比较方式)。
【解决方案2】:

双打的常见比较实现为

bool CompareDoubles2 (double A, double B)
{
   return std::abs(A - B) < 1e-6; // small magic constant here
}

显然不如检查A == B效率高,因为它涉及到更多的步骤,即减法,调用std::abs,最后与一个常数进行比较。

关于效率的相同论点适用于您提出的解决方案:

bool CompareDoubles1 (double A, double B)
{
   double a = round(A,4); // the magic constant hides in the 4
   double b = round(B,4); // and here again
   return a == b;
}

再一次,这不会像直接比较那样有效,但是——再一次——它甚至没有尝试做同样的事情。

CompareDoubles2 或 CompareDoubles1 是否更快取决于您的机器和魔术常数的选择。只是测量它。您需要确保提供匹配的魔法常数,否则您将使用不同的信任区域检查是否相等,这会产生不同的结果。

【讨论】:

  • 不一定是魔法。实际上,我认为它不应该是魔术,而是有意识地选择了一个代表结果精度的变量 =) 您的 15 位精确 0.000001 和 0.000002 神奇地可能是一个非常糟糕且难以追踪的惊喜彼此相等。
  • @luk32 这不是任意意义上的魔法。它确实需要仔细选择。但是,我将它们称为“魔术常量”,因为它们是硬编码的常量,其目的并不直接明显,可能应该用有意义的名称代替。 (c.f.wikipedia on magic numbers)
  • 我知道你的意思。我认为不应在比较器本身中使用这样的常量,而应将其作为参数传递,甚至是可选的。这是一种不好的做法,可能会适得其反。我只是说,它不应该是魔法。以你的魔力 =) 当然,当涉及到真正的事情时,你的权利,你可以那样做,是否应该是另一回事。我的意思是你刚才没有说,事实上这个魔法不好。我认为值得明确指出。
  • @luk32 是的,你是对的。在生产代码中,我也不会写这个。但是这里已经有一个相当长的问题/答案页面,即 OP 链接到的那个页面,它应该提供足够的信息。
  • 该函数需要同时处理1.00000E+20 == 1.00001E+20 和1.00000E-20 == 1.00001E-20。因此,将 epsilon 值作为参数。
【解决方案3】:

我认为将差异与固定容差进行比较是个坏主意。

说如果你将容差设置为 1e-6 会发生什么,但你比较的两个数字是 1.11e-9 和 1.19e-9?

这些将被视为相等,即使它们在第二个有效数字之后不同。这可能不是你想要的。

我认为比较好的方法是

equal = ( fabs(A - B) <= tol*max(fabs(A), fabs(B)) )

请注意,

旁注:当你想测试一个数字是否为零时,上面的测试可能并不理想,那么确实应该使用绝对阈值。

【讨论】:

    【解决方案4】:

    如果您的示例中使用的舍入函数意味着四舍五入到第 4 位小数,则这根本不正确。例如,如果 A 和 B 分别为 0.000003 和 0.000004,则它们将四舍五入为 0.0,因此会被比较为相等。

    通用比较函数不能与恒定公差一起使用,而应与相对公差一起使用。但这一切都在您在问题中引用的帖子中进行了解释。

    【讨论】:

    • 我不明白为什么 0.000003 和 0.000004 应该是一个例子来说明为什么舍入是一个坏主意。通常的|差异|
    • 因为它们相差 33%,通常不应该相等。 Andreas H. 的帖子遵循相同的论点。
    【解决方案5】:

    没有“正确”的方法来比较浮点值(即使 f == 0.0 也可能是正确的)。不同的比较可能是合适的。看看http://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/

    【讨论】:

      【解决方案6】:

      与其他帖子类似,但引入了尺度不变性:如果您正在执行类似将两组数字相加的操作,然后您想知道这两组数字的总和是否相等,您可以取对数的绝对值-比率(对数差)并测试以查看这是否小于您规定的容差。这样,例如如果在求和计算中将所有数字乘以 10 或 100,则不会影响答案是否相等的结果。您应该有一个单独的测试来确定两个数字是否相等,因为它们足够接近 0。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-12-22
        • 2012-02-01
        相关资源
        最近更新 更多