浮点不准确的例子
你如何解释浮点不正确的新程序员和外行谁仍然认为电脑是无限的明智和准确?
你有没有一个最喜欢的例子或轶事,似乎是比精确,但干燥的解释更好的想法?
计算机科学课是如何教授的?
人们基本上有两个主要陷阱,一个是浮点数。
-
规模的问题。 每个FP编号都有一个指数,它决定了数字的整体“比例”,因此您可以表示非常小的值或真正的大数值,尽pipe您可以为此设置的位数是有限的。 添加两个不同比例的数字有时会导致较小的一个被“吃掉”,因为没有办法将它放到更大的比例中。
PS> $a = 1; $b = 0.0000000000000000000000001 PS> Write-Host a=$ab=$b a=1 b=1E-25 PS> $a + $b 1
作为这种情况的比喻,你可以画一个大型的游泳池和一茶匙的水。 两者的尺寸都非常不同,但是您可以轻松掌握它们的粗略程度。 然而,把茶匙倒入游泳池,会让你仍然有一个大概的游泳池。
(如果学习这个的人有指数记号的困难,也可以使用值
1
和100000000000000000000
左右。) -
然后是二进制与十进制表示的问题。 像
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
尽pipe如此,我想不出一个好的比喻来正确地解释这一点。 这基本上是一样的问题,为什么你可以只用十进制来表示1/3 ,因为要得到你需要在小数点末尾无限期地重复3的精确值。
类似地,二进制分数对于表示半数,四分之一,八分之一等是好的,但是像十分之一的事物将产生无限重复的二进制数字stream。
-
那么还有另一个问题,虽然大多数人不会绊倒,除非他们做了大量的数字材料。 但是,那些已经知道这个问题的人。 由于许多浮点数仅仅是精确值的近似值,这意味着对于实数r的给定近似值f ,可以有无限多的实数r 1 , r 2 ,…,其映射到完全相同的近似值。 这些数字在一定的时间间隔内。 假设r min是r的最小可能值,导致f和r max是r的最大可能值,那么你得到了一个间隔[ r min , r max ],其间的任何数字都可以是你的实际数字r 。
现在,如果你对这个数字进行计算 – 加,减,乘等等 – 你会失去精度。 每一个数字都只是一个近似值,所以你实际上是用间隔进行计算的。 结果也是一个时间间隔,近似误差只会变大,从而扩大间隔。 你可以从这个计算中得到一个单一的数字。 但是这只是可能结果间隔中的一个数字,要考虑到原始操作数的精度以及计算的精度损失。
这样的事情叫做区间algorithm ,至less对我来说,这是我们大学math课程的一部分。
向他们表明,基10系统遭受完全相同的问题。
尝试以10为底数的十进制表示1/3。您将无法完全做到这一点。
所以如果你写“0.3333”,你将会有很多用例的合理表示。
但是,如果你把它移回到一个分数,你会得到“3333/10000”,这是不一样的“1/3”。
其他分数,例如1/2,可以很容易地用以10为底的有限十进制表示来表示:“0.5”
现在基地2和基地10遭受本质上相同的问题:都有一些他们不能准确表示的数字。
虽然base-10在base-2中没有将1/10表示为“0.1”的问题,但是您需要以“0.000110011 ..”开头的无限表示。
这对于外行的解释如何? 计算机代表数字的一种方法是通过计算离散单位。 这些是数字电脑。 对于整数,那些没有小数部分的现代数字计算机计算两个幂:1,2,4,8。,,,地点值,二进制数字,等等,等等等等。 对于分数,数字计算机计算两个反函数:1/2,1 / 4,1 / 8,…问题是许多数字不能用有限数量的这些反函数的和表示。 使用更多的位置值(更多位)将会提高这些“问题”数字表示的精度,但从来没有得到它,因为它只有有限的位数。 有些数字不能用无限的位数表示。
打盹…
好的,你要测量一个容器中的水量,你只有三个量杯:满杯,半杯和四分之一杯。 在计算完最后一个满杯之后,假设剩下一杯剩下三分之一。 然而,你不能衡量,因为它不完全填补任何可用杯组合。 它不填满半杯,而四分之一杯的溢出物太小而不能填充任何东西。 所以你有一个错误 – 三分之一和四分之一的差异。 如果将其与其他测量的误差组合在一起,则会出现此错误。
在python中:
>>> 1.0 / 10 0.10000000000000001
解释一些分数如何不能用二进制精确表示。 就像一些分数(如1/3)不能精确地表示在10的基础上。
又如,在C
printf (" %.20f \n", 3.6);
令人难以置信的给
3.60000000000000008882
这是我的简单理解。
问题:0.45的值不能精确地用浮点数表示,并四舍五入为0.450000018。 这是为什么?
答案:二进制值101101表示一个int值45。为了使值为0.45,如果你可以取45×10 ^ -2(= 45/10 ^ 2),那将是准确的。但这是不可能的,因为你必须使用基数2而不是10。
所以最接近10 ^ 2 = 100就是128 = 2 ^ 7。 对于数值7(111),值45(101101)+ 3位需要的总位数是9:6。 那么值45 x 2 ^ -7 = 0.3515625。 现在你有一个严重的不准确的问题。 0.3515625几乎接近0.45。
我们如何改善这种不准确性? 那么我们可以把值45和7改成别的东西。
460×2 ^ -10 = 0.44921875。 你现在正在使用9位的460位和4位的10位。然后再靠近一些,但还没有那么接近。 但是,如果您的初始期望值是0.44921875,那么您将得到一个完全匹配,没有近似值。
所以你的价值公式是X = A×2 ^ B。 其中A和B是整数值正数或负数。 显然,数字越高,精确度就越高,但是正如你所知,代表数值A和B的位数是有限的。 对于浮点数,总共有32个。双精度为64,小数为128。
如果将9999999.4999999999转换成float
并返回double
可能会观察到一个可怕的数字怪异。 结果报告为10000000,尽pipe该值显然更接近于9999999,即使9999999.499999999正确舍入为9999999。