作者| 慕课网精英讲师 咚咚呛
本文首发自「慕课网」,想了解更多IT干货内容,程序员圈内热闻,欢迎关注!
最近有同学在问答区提到了一个很有意思的问题:为什么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.....1
12/2=6......0
6/2=3......0
3/2=1......1
1/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 0
0.25*2=0.5=0.5 0
0.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.0001100110
0.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.5
0.41999约等于 0.4
同样的,在二进制中,有零舍一入的操作。
0.1001(2) 约等于 0.10(2)
0.1011(2) 约等于 0.11(2)
所以在二进制运算中,为了进行有效数值的运算,会对浮点数尾数进行零舍一入的操作,从而导致真实值和计算值的偏差。
这就可以合理的解释在Python中0.1 0.2为什么等于0.3000...4了。
谢谢大家,希望对同学们有所帮助。
欢迎关注「慕课网」,发现更多IT圈优质内容,分享干货知识,帮助你成为更好的程序员!
,