作者| 慕课网精英讲师 咚咚呛
最近有同学在问答区提到了一个很有意思的问题:为什么0.1+0.2 = 0.30000...4?
这个问题是怎么来的呢?有Python环境的同学,只要打开Python终端,输入0.1+0.2,就能得到0.3000000..4的结果。
首先我们知道,计算机是以二进制的方式存储数据的。
比如:5=101(2),15=1111(2),25=11001(2),把十进制转换成二进制的方式叫做重复相除法,过程大致是这样子的。
25/2= 12.....112/2=6......06/2=3......03/2=1......11/2=0......1# 遵循两个原则# 1. 重复相除,直到商为0结束# 2. 余数结果从下往上得到二进制形式
我们也可以按照按权展开法,得到二进制转换成十进制的结果,过程大致是这样子的。
11001= 1*2^4+ 1*2^3+ 1*2^0= 16+ 8+ 1= 25。
不过以上只是整数形式的转换方法,小数在计算机硬件上也是使用二进制表示的,不过转换的方法有些不一样,十进制小数转换成二进制的方法叫做重复相乘法。
一般来说如果一个小数既有整数部分又有小数部分,那么这个小数转换成二进制是分成两个部分转换的,整数部分使用重复相除法,小数部分使用重复相乘法。
重复相乘法的大概过程是这个样子的,比如想把十进制小数0.125转换成二进制。
0.125*2=0.25=0.25+00.25*2=0.5=0.5+00.5*2=1=0.0+1# 注意,这里把结果分成了小于1和大于等于1的两个部分,当小于1的部分=0时,结束运算。# 大于等于1的部分,从上往下得到001即为0.125的小数结果(0.001)。
再举一个复杂一点的例子:
小学我们学过,小数分为有限小数和无限小数,无限小数又分为无限循环小数和无限不循环小数。
同样的,对于二进制小数来说,同样有有限小数和无限小数两大类。那么重点来了,0.1和0.2在十进制小数里面是有限小数,但是使用二进制表示的时候就不是了。
不信你使用重复相乘法计算一下试试:
# 运算可以得到:0.1=0.0001100110011...# 它实际上是一个无限循环小数0.1=0.0(0011) # 括号的0011即是无限循环的部分# 同理得到0.2的二进制0.2=0.(0011) # 括号的0011即是无限循环的部分
所以计算机是无法准确表示0.1和0.2两个数的,只能无限逼近这两个数。假设计算机使用10位精度,则计算机里面,0.1和0.2是这样子的。
0.1=0.0001100110(2)0.2=0.0011001100(2)# 现在我们使用二进制加法,运算两个二进制数0.00011001100.0011001100————————0.0100110010(2)
再把这个结果转换成十进制
0.0100110010(2) = 1*(1/4) + 1*(1/32) + 1*(1/64) + 1*(1/512) = 0.298828125< 3
可以看到这个数值已经很接近3了,如果把精度再调高为16位、32位甚至是64位,会得到更加接近3的结果。这就解释了为什么在十进制中运算和在二进制中运算,会得到不一样的结果。
但是,这就完了吗?还没有。其实可以归纳总结到,不管精度是多少,这个结果应该是恒小于3的,而不可能是大于3的结果,因为在截取有效数时,总是把末尾的数值去掉了,那为什么在Python这里会大于3呢?
这主要是因为除了精度以外,计算机浮点数表示数据的时候还有对阶、零舍一入等等的操作,会使得实际操作数比原操作数大一些。
在十进制中有四舍五入法。比如:
0.499999约等于 0.50.41999约等于 0.4
同样的,在二进制中,有零舍一入的操作。
0.1001(2) 约等于 0.10(2) 0.1011(2) 约等于 0.11(2)
所以在二进制运算中,为了进行有效数值的运算,会对浮点数尾数进行零舍一入的操作,从而导致真实值和计算值的偏差。
这就可以合理的解释在Python中0.1+0.2为什么等于0.3000...4了。
谢谢大家,希望对同学们有所帮助。