浮点数的运算精度丢失
引出
打开Python编译器,输入 0.1+0.2
, 期待的结果是0.3,但是输出为:
0.30000000000000004
有点小尴尬,这是为什么呢?
解惑
其实这设计到了计算机的浮点数存储是以二进制进行存储的。
说二进制不太形象,换成我们最长使用的十进制和分数
1/5,使用小数表示为0.2,但是1/3,使用小数表示就是一个无限循环小数:0.3333333
, 也就是说,分数的 1/3+1/3=2/3,但如果使用小数:0.3333+0.3333=0.6666
, 结果只会无限接近2/3,而不会等于2/3
因为把10分成三份,是不能够整分的。同样,把2分成十份,也不能整分。考虑到2整分只能分成两份,也就是说,二进制只能精确表示十进制小数0.5
十进制到二进制的转换在此略过。
十进制的0.1,转换成二进制是:0.00011001100110011
无限循环的小数,所以二进制的小数运算,就会出现上面的1/3+1/3的情况,无法精确计算,只能够近似表示。那为什么python这些语言,我们在使用的时候没有察觉到这个问题呢?因为编译器自觉的帮我们做了近似的处理。
和十进制无法精确表示分数的1/3同样,二进制也无法精确表示十进制的小数。
分析
为了方便分析,我们讲计算机存储的字节数量进行缩减,我们假设小数点后只能保存8为小数。
十进制的0.1,转换成二进制为:0.00011001 (再反转回十进制,就会发现精度的丢失了,十进制是:0.09765625)
十进制的0.2,转换成二进制为:0.00110011 (反转回十进制,为:0.19921875)
加法运算:
十进制
0.1+0.2=0.3
二进制
0.00011001+0.00110011=0.01001100 (转成十进制:0.296875)
当然,计算机中存储的位数要比8位多,python浮点数占用8个字节,64位。但因为是无限小数,并不是位数多了就会准确。
那么如何做这种精度的计算呢?其实很简单,精度丢失是小数才会有,只要转成整数,就不会有这个问题了。比如Python中:
(1.0+2.0)/10
结果:0.3
, 没毛病。
当然,这个0.3也不是精确的0.3,但会在显示过程进行精度转换,通过整数运算,避免了小数运算过程中的丢失精度问题。