【问题标题】:How seriously can ceil, floor and round functions amplify loss-of-precision issues, and how to eliminate such effects?ceil、floor 和 round 函数会在多大程度上放大精度损失问题,以及如何消除这种影响?
【发布时间】:2019-07-30 03:56:23
【问题描述】:

我之前发布了一个关于 C++ 函数ceil()floor()round() 的精度损失问题的问题。问题的链接如下。

Will ceil(), floor() and round() meet loss-of-precision problems when converted to integers?

从我收到的两个答案中,我了解到这些函数本身是精确的,但它们会放大现有的精度损失问题。

我想知道这些问题会被放大到多大程度。具体来说,请考虑以下几点。

a = fun(b);

这里,a 是整数类型值(intlonglong long 等),b 是浮点类型值(floatdouble 等),并且fun()ceil()floor()round() 之一。请注意,在以下所有讨论中,我假设不存在上溢或下溢问题。

请注意,在这里,我假设 b 是通过其他可能导致精度损失的操作获得的。例如,假设我希望 b 等于 1.1 + 3.9 = 5,但是由于无法表示 1.1 和 3.9,所以 b 不会以 5 结尾。

我想知道这些问题会在多大程度上影响 a 的价值。具体来说,a 的正确值(即,如果 b 没有精度损失)与实际获得的值(即,如果存在潜在的精度损失)相差多少。假设我们将正确的值表示为 a1,将实际值表示为 a2。在我看来,在任何情况下,以下结论都必须成立。

  1. 如果fun()ceil(),则a2 等于a1 或a1 + 1。
  2. 如果 fun()floor(),则 a2 等于 a1 或 a1 - 1。
  3. 如果fun()round(),则a2 等于a1。

我的问题是,如果我们忽略溢出和下溢

  1. 上述结论总是成立吗?
  2. 如果无法修复 b 的精度损失,如何修改代码以确保 a2 始终等于 a1?

【问题讨论】:

  • 如果您询问的是 C++,请不要包含 C 标签,反之亦然。

标签: c++


【解决方案1】:

首先,您假设您的错误是b 小。如果b 中的错误大于 1.0,那么您的任何结论都不会成立。但大概在实践中b 的错误很小,这是一个合理的假设。

然而,结论 3 在所有情况下都是错误的。正确的结果是 a2 等于 a1 - 1,或 a1 + 1,或 a1。假设 b 的正确值为 3.49999,实际值为 3.50001,则当正确结果为 3 时,round 将给出 4。反之亦然,如果正确值为 3.50001,实际值为 3.49999,则round 将给出 3当正确结果为 4 时。

至于问题 2,没有简单的答案。并且不可能笼统地回答这个问题,因此需要对您的实际问题进行一些具体说明。听起来您在需要精确结果时尝试使用不精确的算术。也许你需要切换到某种多精度算术,有几个库可以做到这一点,例如MPIR

【讨论】:

  • 感谢您的回复。还有一个问题,假设我正在对双精度/浮点精度值进行一系列操作。所有的初始值、中间结果和最终结果都是可表示的,那么我保证得到一个精确的结果吗?例如,考虑代码 a = b * c + d / e。假设b、c、d、e的值是精确的,b*c、d/e和b*c+d/e的精确值都是可表示的,我能保证a是精确的吗?
  • 是的,我相信。 IEEE754 标准保证任何操作的结果都是最准确的。因此,如果结果完全可以表示,那么这就是您应该得到的结果。需要注意的是,我不确定库函数 sincossqrt 等的保证是什么,它们可能会返回不准确的结果。
  • 你确定吗,中间结果是可表示的?如果是这样,这是一个非常不寻常的情况。我只是在检查您是否意识到即使像十分之一这样的简单分数也不能表示为浮点数。
  • 其实我相信我就是这种情况。基本上,我的代码类似于 int_1 = int_2 * int_3 * log2(int_4) + int_5 * (log2(int_4) + ceil(log2(int_3)));这里 log2 和 ceil 返回双精度值。如果精确计算 log2(int_4) 应该等于一个整数(我通过比较 (int)log2(int_4)、(int)(ceil(log2(int_4))) 和 (int)(floor(log2(int_4 ))),它们共享相同的值)。似乎我对 ceil(log2(int_3)) 的精度无能为力,所以我不得不假设它是精确的。而且似乎其余的中间结果是可以表示的。
  • 这里的int_1到int_5都是int类型的值。
【解决方案2】:

让我换个说法。

假设 b_exact 是一个精确实数,b 是它的浮点表示,a_exact 是 func(b_exact) 的结果,如果以无限精度计算,并且 a = func(b)。你想知道|a_exact -a | 有多大,或者|a_exact - a|/|a_exact|可以。

查看表格here,第 7.4 节,了解 IEEE 浮点表示所产生的最大绝对误差和最大相对误差。

【讨论】:

  • 请查看更新后的问题。我知道 fun() (天花板、地板或圆形)会产生精确的值。我关心的是,如果 b 不精确,fun() 会在多大程度上放大这种不精确性。
猜你喜欢
  • 2023-03-09
  • 2012-02-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多