【问题标题】:Exact double division精确双除法
【发布时间】:2022-01-08 17:10:00
【问题描述】:

考虑以下函数:

auto f(double a, double b) -> int
{
  return std::floor(a/b);
}

所以我想计算最大整数k 使得k * b <= a 在数学意义上。 由于可能存在舍入错误,我不确定上述函数是否真的计算了这个k。我不担心k 可能超出范围的情况。 确定这个k 的正确方法是什么?

【问题讨论】:

  • double 的值范围比int 的大,所以有些值对于int 来说太大或太小。你没有以任何方式处理那个案子。也就是说,我过去做过类似的事情,只是为极端情况编写了单元测试。
  • 我建议使用您的函数查找 k,然后检查 k-1、k 和 k+1 是否满足条件。然后选择满足的最大的。 (您可能需要使用 long long)

标签: c++ rounding-error floor-division


【解决方案1】:

这取决于你有多严格。取一个 double b 和一个整数 n,并计算 bn。然后 a 将被四舍五入。如果 a 向下舍入,则它小于 nb 的数学值,并且 a/b 在数学上小于 n。如果 n 而不是 n-1,您将得到一个结果。

另一方面,a == b*n 为真。所以“正确”的结果可能会令人惊讶。

你的条件是“kb b 的结果是

【讨论】:

  • 所以你是说这个函数总是产生正确的结果?为什么n 不能是最近的浮点数?
  • 抱歉,必须纠正这个问题。当 a == bn 由于四舍五入时,您会得到“错误”的结果,但在数学上是 a/b b
  • 除法时,我们生成的数字总是小于实际答案。尽管double 打印的数字可能会向上取整,但在完整的内部表示中,我们总是可以说它小于向上取整的整数。
  • 我看到只有 n 非常高的四舍五入,整数范围很好。 ``` 双 n = pow(10,23);双b = 999999999999999;双a = n * b; printf("%.17lf %17lf\n",a,b); printf("%.17lf %lf\n",a/b,floor(a/b)); ````````````````39999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999991611392.0000000000000000000 999999999999999916113929999991611392.000000```hab
【解决方案2】:

问题是浮点除法不准确。

a/b 可以给1.9999 而不是2,然后std::floor 可以给1

一个简单的解决方案是在调用std::floor之前添加一个小值:

std::floor (a/b + 1.0e-10);

结果:

result = 10 while 11 was expected
With eps added, result = 11

测试代码:


#include <iostream>
#include <cmath>

int main () {
    double b = atan (1.0);
    int x = 11;
    double a = x * b;
    int y = std::floor (a/b);
    std::cout << "result = " << y << " while " << x << " was expected\n";
    
    double eps = 1.0e-10;
    int z = std::floor (a/b + eps);
    std::cout << "With eps added, result = " << z << "\n";
    
    return 0;
}

【讨论】:

  • 请注意,我在这里假设该比率是正数。如果是否定的,则进行检查/调整。
  • 我怀疑这种方法是否可行,因为如果a = b-eps,您的结果会发生变化。那么答案是k=0,你会得到k=1
  • 没有。如果数学上精确的结果是 2,则不能四舍五入为 1.999999999。舍入永远不会舍入“过去”的浮点数,因此结果永远不会太小。
  • 在实践中,我已经使用这种方法几十年了。一个问题是您在计算ab 时存在舍入误差,并且在计算a/b 时存在舍入误差。没有办法对它们进行后验区分。当这种方法有效地失败时,您可以找到极端情况,但是,在这些情况下,解决它们的唯一方法可能是使用具有更高位数的数字表示。
  • @gnasher729 2 不能直接四舍五入为 1.999999。但是理论上应该给出 2 的计算可以给出 1.999999。我的措辞可能不准确。看代码示例。
猜你喜欢
  • 2020-02-29
  • 1970-01-01
  • 1970-01-01
  • 2012-05-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-10-16
相关资源
最近更新 更多