震惊!小 bug 引发大灾难,0.1 + 0.2 的结果竟然是……

各位观众看文章的时候,我或许正在小黑屋默默的反思自己为什么要成为一个标题党!

好了,说正事

请大家思考一下在 python 控制台输入 0.1 + 0.2 == 0.3 ,最终的结果为
True 还是 False
手边有电脑的同学可以立即在 python 控制台下尝试一下,对浮点数精度不够了解的同学可能会大呼:天啦噜,夭寿啦,怎么会是 False
没错 ,不管是在 Python 、Java、JavaScript 还是其他任何语言中,都是 False

为什么会出现这样的结果,首先我们要明白,在计算机的存储类型为二进制,十进制的 0.1 与 0.2 在计算机中会已二进制的形式表示,规则如下:

十进制小数转换成二进制小数采用”乘2取整,顺序排列”法。具体做法是:用2乘十进制小数,可以得到积,将积的整数部分取出,再用2乘余下的小数 部分,又得到一个积,再将积的整数部分取出,如此进行,直到积中的小数部分为零,或者达到所要求的精度为止。

以 0.1 为例,我们做一下转换:

步数算式结果
10.1 * 2 = 0.2取 0
20.2 * 2 = 0.4取 0
30.4 * 2 = 0.8取 0
40.8 * 2 = 1.6取 1
50.6 * 2 = 1.2取 1
60.2 * 2 = 0.4取 0
70.4 * 2 = 0.8取 0
80.8 * 2 = 1.6取 1
……

比较第二步和第六步,可以得知, 已二进制 表示 0.1 最终的结果为一个无限循环的数
0.0001100110011…… ,但由于计算机的存储位数是有限的,并不能存储一个无限循环的数。对于 Python 来说,浮点数有 53 位精度。为了把这个数存起来,必然会丢失部分精度,造成误差,所以最终的近似结果为:

0.00011001100110011001100110011001100110011001100110011010

同理, 对 0.2 的处理也是一样,所以当两个存在误差的数相加,其结果也必定出现误差,这也很好的解释了在计算机中为什么 0.1 + 0.2 不等于 0.3 !

浮点数精度的知识远不止此,摊开来讲一本书也讲不完,所以对于初学者来说只要知道有这么回事就行了,之后再遇到就不要惊讶了,日常工作中遵循以下准则:

1、尽量避免使用小数比较大小,比较两个小数是否相等时可写成 abs(a - b) < 0.000001
2、确保数组的索引都是整数。
3、按分(而不是元)计算金额。百分比放大100倍计算以避免出现小数。
4、Python3 使用除法(/)时需注意,它的结果总是小数,整除的符号是 //。
5、避免在同一个表达式中使用相差太大或太小的数值。将很小的数值和很大数值相加,小的数值很可能被当作0。

当然,在某些科研、财务等对精度要求比较高的领域中,Python 提供 decimal 模块准确控制精度。

它具有以下特点:

1、提供十进制数据类型,并且存储为十进制数序列;
2、有界精度:用于存储数字的位数是固定的,可以通过 decimal.getcontext().prec=x来设定,不同的数字可以有不同的精度

可以通过整数、字符串或者元组构建 decimal.Decimal,对于浮点数需要先将其转换为字符串


>>>from decimal import *
设置精度为 7 位
>>>getcontext().prec = 7
>>>Decimal(1) / Decimal(7)
Decimal('0.1428571')

参考文章:

Python官方对于浮点数的解释:https://docs.python.org/2/tutorial/floatingpoint.html
decimal 模块:https://docs.python.org/2/library/decimal.html
代码之谜:http://justjavac.iteye.com/blog/1724438
Android计算器低级错误?都是二进制惹的祸!:http://www.guokr.com/article/27173/

    原文作者:zx576
    原文地址: https://www.jianshu.com/p/c2ffd2fcb49e
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞