一、问题说明
在Python中定义两个浮点型进行计算,会发现某些时候计算并不准确。如下图的代码,并没有得到预期的0.3,而是一个无限接近0.3的数值。
为什么会出现这种情况呢?
二、问题原因
查阅各方资料得知,由于所有数据在计算机中都是以0和1形式存储的,在机器字长有限的情况下,浮点型的精度也是有限的。浮点型在计算机中的存储一般遵从IEEE 754标准。
IEEE 754标准:
IEEE 754标准规定浮点数的存储包括三部分,分别为“数符”(表示数值正负)、“阶码”和“尾数”。(类似于科学计数法)
IEEE 754标准规定常用的浮点数格式有短浮点数(单精度、float型)、长浮点数(双精度、double型)和临时浮点数。
各类浮点数的位长如下:
IEEE 754标准的浮点数中(临时浮点数除外),尾数采取隐藏位策略的原码表示,阶码用移码表示。
即规格化长浮点数的真值为:(-1S)×1.M×2E-1023
其中S=0为正数,S=1为负数。
浮点数的表示范围如下:
上述标准大致意思是说二进制对很多浮点型数据无法准确表示只能用一个近似值代替,而当使用这些近似值代替的浮点数进行运算时,本质上是这些近似值参与运算,出来的结果也就是近似结果。
也就说明了浮点数运算不是算术的问题,是由浮点数存储方式导致。同时这也不是Python语言的问题,而是C、Java等其他计算机语言共有的问题。
那么,为什么二进制可以表示2,不能表示0.2呢?
这是因为数值和字符串是不一样的,如果是字符串,那么表示2.2的左右两边编码一样就行(如ASCII码:504650),单数值不同,数值的整数部分和小数部分需要统一的表示形式,那就是加权位计数法。
整数部分都要以2的0次方(20)到2的无穷次方(2∞)表示,这没有问题,只要长度足够就能表示出所有奇数和偶数。2 = 1 * 21 + 0 * 20 = 10
小数部分都要以2的-1次方(2-1)到2的负无穷次方(2-∞)表示,这就有问题,因为比如2-1…2-∞不管怎么组合都不能完全等于0.2。0.2 = 0 * 2-1 + 0 * 2-2 + 1 * 2-3 …
三、处理方法
如果想要计算精确,python提供了Decimal()方法让浮点运算结果可以和人平时运算的结果一样。(Decimal本质应该还是通过加长长度提高精度),但需要注意Decimal传字符串才能准确表示。