【问题标题】:Double precision comparison in c fails [duplicate]c中的双精度比较失败[重复]
【发布时间】:2012-12-06 20:06:46
【问题描述】:

可能重复:
Why “for( i = 0.1 ; i != 1.0 ; i += 0.1)” doesn’t break at i = 1.0?

我有一个数字(实数)区间 [x, y]。我必须使用类似的东西遍历它:

nr = 0;
for (i = x; i <= y; i += step) //step is a small double value
    nr++;

对于步长为 0.001 的 [-1, 1],很明显 nr 应该是 2001(-1.000 ... 0.999 1.000),但是它计算出 nr = 2000(我进行了调查,但最后一次比较失败:0.999 + 0.001 > 1.000)

如何计算准确的 nr 值?

【问题讨论】:

  • float/double 本质上是不精确的;如果您需要“精确”,请使用整数类型
  • 您真的只想计算步数,还是循环体中的步数更多?里面会有什么?
  • nr 表示图像的高度,它映射在真实平面上。我在 for(s) 中计算了一些像素值。我知道精确计算 nr 是不可能的,所以我会坚持“height = (int) ((y - x) / step)。
  • @BoPersson:这个问题并没有问为什么总和不正确,就像你提出的重复一样。这个问题,如果从字面上看,询问如何准确计算 floor((y-x)/step)。

标签: c floating-point double precision


【解决方案1】:

您需要使用整数来执行此操作,或者对不精确感到满意,因为浮点数和双精度数本质上是不精确的*。您的代码完全按照 C 语言规范工作。在需要精确精度的情况下使用浮点数和双精度数是不安全的。

除此之外,您还需要在问题中提供有关您的要求的更多信息。如果nr 是一个温度,那么要计算你需要使用不存在或不存在的物理学的确切值(我不是物理学家,所以我不能告诉你)。如果step 实际上是一个不合理的值,那么您必须向上或向下舍入 - 您的调用以及哪个正确取决于您的业务逻辑要求。如果它是合理的并且不需要四舍五入,那么您需要乘以最小公分母并使用 ints(或 C 中的分数类等)。

*当分数的分母是 2 的幂时,每条评论的浮点数/双精度数是精确的,如果你可以强制 stepy 符合这一点,你的问题就会很清楚,但当然如果 @ 987654326@ 不会平均分为y,您需要执行与上述完全相同的业务逻辑处理。

【讨论】:

  • 你会如何解决这个问题?
  • 我不知道,因为我不知道您的应用程序要求是什么。另外,你试过什么?
  • 这既不能回答问题,也不能解释为什么代码不能按预期运行。
  • @EricPostpischil - 公平,添加了评论中发布的不精确信息以及 OP 可能处理但未指定的所有可能情况的相应解决方案。
  • “不精确”作为浮点泛化是无效的。浮点数在某些条件下不精确,而在其他条件下则不精确。看我的回答。
【解决方案2】:
int nsteps = (int)((y-x)/step);
for(int i=0; nsteps; ++i){
   double v = x + (i*step);  // use v in your calculations
}

【讨论】:

  • 我们应该考虑xystep的值,这可能会产生nsteps的不正确值。
  • 这实际上是我现在正在使用的(尽力而为)解决方案。但是,它会计算 nr = 2000。另外,在每次迭代中避免 i * step 乘法不是更快吗?
  • @gg.kaspersky: i*step 在典型处理器上并不昂贵,除非您正在使用一些功率不足的嵌入式处理器。
  • C 的美妙之处在于您可以控制精度(使用 int 和乘法循环)或速度(使用双精度和不乘法的循环)之间的权衡
【解决方案3】:

您遇到的问题是浮点数(floatdouble)未准确存储,因此内存中的表示往往比不准确。

因此您有两个选择 - 使用精确数字或使用整数作为循环次数,然后计算值。

这可以按如下方式完成:

int numberOfIntervals = 2000;
double lowestValue = -1.0;
for (loop = 0; loop <= numberOfIntervals; ++loop)
{
    double v = (0.001 * loop ) + lowestValue;
}

【讨论】:

  • 在堆栈溢出中提出类似问题的许多情况下,循环显然会转换为整数。然而,在这种情况下,x 和 y 是任意浮点值,如何实现所需的计算并不是很明显。这里实际上有一个有趣的问题,它应该得到的不仅仅是简短的回答。
【解决方案4】:

二进制浮点不精确是不正确的。正确的是,对于内容符合 2 的不同(正和/或负)幂之和的数字,该值将是精确的。显然,十进制数符合十的幂的总和,所以它们不会是精确的。以四、八和十六为底符合,因为它们是二的倍数。

我在很久以前发布了this answer,它会给你一个关于浮点数有多精确的提示。您还可以通过对 2 的负幂求和来查看分数如何组合。

至于如何解决问题,您拥有所需的工具。如果算法不适合,你可以让它适合。

【讨论】:

  • 实际上,十进制数也符合 2 的幂和:11 = 8 + 2 + 1,我们可以使用二进制以及任何一个基数来编码它们的确切值。区别不是 int 二进制/十进制基表示,而是整数/实数。
  • 那么,如果十进制数也符合 2 的幂,为什么你的程序不能工作?
  • 我误解了你所说的(我的英语很差)。我以为你的意思是整数十进制数,但你的意思是小数。
  • 您的原始问题包含包含整数和分数的十进制数的问题。这就是我的回答。
猜你喜欢
  • 2015-12-17
  • 2016-12-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-10-22
  • 1970-01-01
  • 2018-06-05
  • 1970-01-01
相关资源
最近更新 更多