浮点不准确性示例

Floating point inaccuracy examples

提问人: 提问时间:1/20/2010 最后编辑:4 revs, 3 users 57%David Rutten 更新时间:5/6/2013 访问量:126482

问:

已锁定。这个问题及其答案被锁定,因为这个问题偏离了主题,但具有历史意义。它目前不接受新的答案或交互。

你如何向那些仍然认为计算机具有无限智慧和准确性的新程序员和外行解释浮点的不准确性?
你有没有最喜欢的例子或轶事,它似乎比一个精确但枯燥的解释更能传达这个想法?
这在计算机科学课上是如何教授的?

点浮 点精度

评论

1赞 4/25/2010
你可以用这个简单的javascript来证明这一点:alert(0.1*0.1*10);
0赞 Rubens Farias 1/20/2010
看看这篇文章: 每个计算机科学家都应该知道的浮点运算

答:

26赞 5 revsЈοеу #1

浮点数基本上有两个主要陷阱。

  1. 规模问题。每个 FP 数字都有一个指数,它决定了数字的整体“比例”,因此您可以表示非常小的值或非常大的值,尽管您可以为此投入的位数是有限的。将两个不同比例的数字相加有时会导致较小的数字被“吃掉”,因为没有办法将其放入较大的比例中。

    PS> $a = 1; $b = 0.0000000000000000000000001
    PS> Write-Host a=$a b=$b
    a=1 b=1E-25
    PS> $a + $b
    1
    

    作为这种情况的类比,您可以想象一个大型游泳池和一茶匙水。两者的大小都非常不同,但单独使用时,您可以很容易地掌握它们的大致数量。然而,将茶匙倒入游泳池中,您仍然会留下一个装满水的游泳池。

    (如果学习这个的人在指数表示法方面有困难,也可以使用值和/或左右。1100000000000000000000

  2. 然后是二进制与十进制表示的问题。像这样的数字不能用有限数量的二进制数字精确表示。但是,有些语言会掩盖这一点:0.1

    PS> "{0:N50}" -f 0.1
    0.10000000000000000000000000000000000000000000000000
    

    但是你可以通过重复将数字相加来“放大”表示错误:

    PS> $sum = 0; for ($i = 0; $i -lt 100; $i++) { $sum += 0.1 }; $sum
    9,99999999999998
    

    不过,我想不出一个很好的类比来正确解释这一点。这基本上是同一个问题,为什么你只能用十进制来表示 1/3,因为要获得确切的值,你需要在小数部分的末尾无限重复 3

    同样,二进制分数适用于表示二分之一、四分之一、八分之一等,但像十分之一这样的东西会产生无限重复的二进制数字流。

  3. 然后还有另一个问题,尽管大多数人不会偶然发现这个问题,除非他们正在做大量的数字工作。但是,那些人已经知道了这个问题。由于许多浮点数只是精确值的近似值,这意味着对于实数 r 的给定近似值 f,可以有无限多的实数 r 1r2、...映射到完全相同的近似值。这些数字位于一定的区间内。假设 r min 是 r 的最小可能值,导致 f 和 r max 是 r 的最大可能值,因此你得到一个区间 [r min, r max],其中该区间中的任何数字都可以是您的实际数字 r

    现在,如果你对这个数字进行计算——加法、减法、乘法等——你就会失去精度。每个数字都只是一个近似值,因此您实际上是在执行带有区间的计算。结果也是一个区间,近似误差只会变大,从而扩大区间。您可以从该计算中返回一个数字。但这只是可能结果区间中的一个数字,考虑到原始操作数的精度和由于计算而导致的精度损失。

    这种东西被称为区间算术,至少对我来说,这是我们大学数学课程的一部分。

评论

1赞 David Rutten 1/20/2010
嗨,约翰内斯,这绝对是一个很好的例子,但它并没有真正告诉人们为什么它不起作用。我希望有人了解失败的原因,而不仅仅是它时不时地失败的事实。
1赞 Joey 1/20/2010
嗯,除了解释比例问题和二进制与十进制表示的问题之外,我想我还没有找到更好的方法来告诉人们这一点:/。人们可能会使用类似的轶事,例如在游泳池中加入一茶匙水并不能改变我们对其中有多少的看法。
0赞 David Rutten 1/20/2010
详细地说,我在研讨会上遇到的许多人甚至对科学记数法都不太熟悉,所以他们已经需要相当多的脑力劳动来理解-4e200、-4e-200、4e-200和4e200之间的区别。
0赞 David Rutten 1/20/2010
瞧,游泳池的比喻正是我想要的那种东西!
1赞 Joey 1/20/2010
@David:好的,把它纳入了答案,并详细说明了一些。尽管如此,找到合适的类比和易于理解的解释并不容易。
2赞 codeape #2

在 python 中:

>>> 1.0 / 10
0.10000000000000001

解释为什么某些分数不能用二进制精确表示。就像某些分数(如 1/3)不能以 10 为基数精确表示一样。

评论

0赞 David Rutten 1/20/2010
codeape,我正在寻找一些更深入的东西,而不仅仅是展示舍入错误的例子。我希望能够告诉人们为什么会出现这些错误,并让他们了解其背后的原因,而无需了解 IEEE 754 规范。
1赞 Joachim Sauer 1/20/2010
@David:给他们一个浮点数精确的例子,例如多次添加 0.25。结果将是准确的,直到你溢出尾数,因为 0.25 是 .然后用 0.2 尝试同样的事情,你会得到问题,因为 0.2 不能用有限的以 2 为底数来表示。1/(2^2)
6赞 gary #3

这怎么适合外行人进行移植。计算机表示数字的一种方式是计算离散单位。这些是数字计算机。对于整数,即那些没有小数部分的数,现代数字计算机计算 2 的幂:1、2、4、8。 ,,, 位值,二进制数字,等等,等等,等等。对于分数,数字计算机计算两个的反幂:1/2、1/4、1/8、...问题在于,许多数字不能用有限数量的反幂之和来表示。使用更多的位值(更多的位)将提高这些“问题”数字的表示精度,但永远无法准确获得它,因为它只有有限的位数。有些数字不能用无限位数的位数表示。

打盹。。。

好的,您想测量容器中的水量,而您只有 3 个量杯:满杯、半杯和四分之一杯。在数完最后一个满杯子后,假设还剩下三分之一的杯子。然而,你无法测量它,因为它不能完全填充任何可用杯子的组合。它不能装满半杯,四分之一杯的溢出量太小,无法装满任何东西。所以你有一个错误 - 1/3 和 1/4 之间的差异。当您将此误差与其他测量误差相结合时,此误差会更加复杂。

8赞 Joachim Sauer #4

向他们展示 base-10 系统也存在完全相同的问题。

尝试将 1/3 表示为以 10 为基数的十进制表示。您将无法完全做到这一点。

因此,如果您编写“0.3333”,您将在许多用例中拥有相当精确的表示。

但是,如果你把它移回分数,你会得到“3333/10000”,这与“1/3”不同

其他分数,例如 1/2,可以很容易地用以 10 为基数的有限十进制表示:“0.5”

现在,以 2 为基数和以 10 为基数的 base 10 存在着本质上相同的问题:两者都有一些它们无法准确表示的数字。

虽然以 10 为基数将 1/10 表示为 “0.1” 没有问题,但在 base-2 中,您需要以“0.000110011..”开头的无限表示。

2赞 cibercitizen1 #5

另一个例子,在 C 语言中

printf (" %.20f \n", 3.6);

难以置信地给予

3.60000000000000008882

0赞 supercat #6

如果将 9999999.499999999999 转换为 a 并返回 a,可能会观察到一个可爱的数字怪异。结果报告为 10000000,即使该值明显更接近 9999999,并且即使 9999999.4999999999 正确舍入为 9999999。floatdouble

1赞 Jan #7

这是我的简单理解。

问题: 值 0.45 不能用浮点数准确表示,而是向上舍入到 0.450000018。为什么?

答: int 值 45 由二进制值 101101 表示。 为了使值为 0.45,如果可以取 45 x 10^-2 (= 45 / 10^2),那将是准确的。 但这是不可能的,因为您必须使用以 2 为基数而不是 10 的基数。

因此,最接近 10^2 = 100 的是 128 = 2^7。值 45 (101101) 需要的总位数为 9 : 6 + 值 7 (111) 为 3 位。 则值 45 x 2^-7 = 0.3515625。现在你有一个严重的不准确问题。0.3515625 并不接近 0.45。

我们如何改善这种不准确性?好吧,我们可以将值 45 和 7 更改为其他值。

460 x 2^-10 = 0.44921875 怎么样。您现在将 9 位用于 460,将 4 位用于 10。然后它更近了一点,但仍然没有那么近。但是,如果初始期望值为 0.44921875,则将得到没有近似值的完全匹配。

因此,您的值的公式将是 X = A x 2^B。其中 A 和 B 是整数值正数或负数。 显然,数字越高,您的精度就越高,但是,如您所知,表示值 A 和 B 的位数是有限的。对于浮点数,您总共有 32 个。Double 有 64,Decimal 有 128。