代码之家  ›  专栏  ›  技术社区  ›  John M Gant aman_novice

C++双倍:除以100会造成非常小的误差

  •  0
  • John M Gant aman_novice  · 技术社区  · 14 年前

    我对这段代码中的特定值集有问题。

    double inputs[] = {0, -546543, 99015, 6750, 825, 2725, 70475, 
        50950, 42200, 6750, 26925, 16125, 134350, 10075, 79378};
    double result = 0;
    for (int i = 0; i < 15; i++) {
        result += inputs[i]/100;
    }
    

    我期望的最终价值 result 0岁。如果我把除法除以100,它就是。但当我把每一个值除以100再加上 结果 最后我得到了-6.8212102632969618E-013。

    关于浮点运算我有很多不明白的地方。我知道这不能保证完全准确。但这个数据集似乎没有任何异常,没有任何非常大或非常小的值,所以我很惊讶计算出了错误。

    有人能向我解释一下这个问题,并就如何避免这个问题提出建议吗?我提出的代码被简化了;在实际的代码中,我不能只除以100,也不能很容易地将数字作为整数相加,然后再除以。

    如有任何建议,我们将不胜感激。

    5 回复  |  直到 14 年前
        1
  •  5
  •   Carl Norum    14 年前

    我不能很容易地把数字加成整数,然后再除以。

    为什么不?这听起来正是解决你问题的办法。加整数和除法一次可能比加浮点数和除法快得多。

    每次除以100,都会累积错误(因为100不是2的幂次)。你所有的数字在 double 但是当你把它们分开的时候,它们就不存在了——这就是你的错误。除了修改您的算法之外,您真的没有什么可以做的。

    在你的例子中,因为你除以100,你可以把最后的和四舍五入到最接近的100,得到正确的结果。

        2
  •  2
  •   ollb    14 年前

    您可能不认为Double使用base-2作为内部表示。因此,尽管乍一看这些值看起来不是很精确或很小,但使用base-2表示它们可能非常困难。

    取1/3=0.333…例如。不能用以10为基数的有限位数精确地描述该值(除非将其存储为分数,但我们将其放在一边)。同样的道理也适用于以2为基数的某些值,这些值看起来和以10为基数的数字一样好。

    0.01就是这样一个例子。要将其存储在base-2中,需要无限个数字,因此1.0/100.0不精确(如float或double)。用10为底的整数除以100,就可以知道这会导致什么。

    一些编程语言为财务计算提供了一个以10为基数的浮点类型(例如以C表示的十进制)。它更适合于我们习惯使用的10基材料,但对计算机来说更贵。当然,还有一些数字无法表示(1/3,…)。

        3
  •  1
  •   Michael Borgwardt    14 年前

    The Floating-Point Guide ,那么你就会明白:

    在内部,计算机使用一种格式 (二进制浮点)不能 准确地表示一个数字 0.1、0.2或0.3。

    基本上,每个中间值(除以100)都有舍入误差。最终的结果也保留了一个错误,这一点也不罕见。

    十进制数字不能准确 表示1/3这样的数字,所以 必须四舍五入到0.33左右- 你不期望0.33+0.33+ 0.33加起来等于1,是吗?

    我该怎么做才能避免这个问题?

    这取决于你在做什么样的计算。

    • 如果你真的需要你的结果精确地加起来,特别是当你使用money时:使用一个特殊的decimal数据类型。
    • 如果您不想看到所有这些额外的小数点:只需在显示结果时将结果四舍五入为固定的小数点位数。
    • 如果没有十进制数据类型可用,则另一种方法是使用整数,例如完全用美分计算货币。但这是更多的工作,也有一些缺点。

    注意第一种情况(需要具体说明 十进制的 结果) 适用于大多数情况。

        4
  •  1
  •   rlduffy    14 年前

    通常,在比较不同实现中的浮点数时,可以建立某种类型的通过/失败标准来确定相等性(或零)。比如10^10表示双精度,10^6表示单精度。例如,如果您将matlab结果与应用程序代码生成的结果进行比较,并确认不可避免的精度错误,则需要一些这样的基准。

        5
  •  0
  •   Andre Holzner    14 年前

    如果必须使用浮点数,则可能需要:

    • 先分别加正和负,再减去和

    • 求和后除以100

    据我所知,并不能保证内存和CPU中浮点寄存器的宽度是相同的(即,当转移到宽度较小的位置时,可能会发生一些取整),存储值的位置取决于编译器生成的机器代码。