原载于https://mp.weixin.qq.com/s/jcgc7lgOIQgZmxzsiVPMUg
2. 精确的小数
二进制可以表示连续的整数,但是却不能够表示连续的小数。
在计算机里面,一个数的小数部分用二进制来表示就是:
s=a12-1+a22-2+...+an2-n
其中 ai的取值是0或者1。 而 a1 a2... an这个序列就是小数s的二进制编码。
要得到二进制编码,可以循环把s乘以2
2s=a1+a22-1+...+an2-n+1
因为a1是0或者1,所以2s的整数部分就是a1。这个时候继续把小数部分乘以2,整数部分就是 a2。这样循环下去就可以得到一个小数的二进制编码。
用上面的方法来计算0.1的二进制编码为:
0.0001100110011…
0011这四个编码可以循环到无数。0.1在二进制编码里面,就这样变成了一个无限循环小数。而计算机是有限存储的,必然有很多的0011被省略了,因此可以得出一个结论,0.1这个小数是无法在计算机中用二进制编码精确表示的。
这样就导致了一个现象。
>>> 0.1+0.2
0.30000000000000004
使用float类型来存储小数,两个小数相加之后居然产生了一个很长的尾巴。
在处理财务数据的过程中,这样的误差是不可容忍的。因为即使这样小的误差,经过积累后也会产生极大的偏差。必须得有一种数据存储方式,是使用十进制来存储的。
Decimal就是专门设计来处理这个问题的。Decimal使用4bits来表示一位小数位数,在存储上有浪费,但是保证了每一位小数的位数都能按十进制进行存储。
使用Decimal很简单,导入decimal模块之后,就可以构造decimal数进行运算。刚才的代码,如果用decimal来计算的话,就不会出现错误了。
>>>from decimal import *
>>>Decimal('0.1')+Decimal('0.2')
Decimal('0.3')
2.1 decimal的构造
要使用decimal必须先导入decimal模块,
>>>from decimal import *
每一个数都需要通过Decimal()进行构造。
可以传一个整数进去进行构造
>>> Decimal(99)
Decimal('99')
可以传一个字符串进去进行构造
>>> Decimal('0.1')
Decimal('0.1')
可以传一个元组进去进行构造
>>>Decimal((0,(1,1,0),-2))
Decimal('1.10')
其中元组第一个参数为符号,0是正号,1是负号;第二个参数(1,1,0)是存储的数;第三个数-2是指数。
从上面的例子也可以看出,用字符串来构造decimal数是最安全也很容易读懂。所以推荐传一个字符串给Decimal构造函数来构造一个decimal数。
这里要特别指出的是,千万不要通过不精确的float来构造,因为float为不精确的数,构造的数自然也是不精确的。
>>> Decimal(0.1)
Decimal('0.1000000000000000055511151231257827021181583404541015625')
2.2 decimal的运算
decimal数之间可以进行多种基本运算,例如相加,相减,相乘,相除,取绝对值,取幂。
>>>a=Decimal('-0.9') >>>b=Decimal('0.1') >>>a+b Decimal('-0.8') >>> a-b Decimal('-1.0') >>> a*b
Decimal('-0.09') >>> a/b Decimal('-9') >>> abs(a) Decimal('0.9') >>> pow(a,2)
Decimal('0.81')
decimal数和整型数也可以进行直接的运算
>>> Decimal('0.1')+7
Decimal('7.1')
>>> Decimal('0.1')*7
Decimal('0.7')
decimal和float之间的运算是不允许的,毕竟float并不精确,运算的结果也不会精确。
>>>Decimal('0.1')+0.2
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError:
unsupported operand type(s) for +: 'Decimal' and 'float'
如果只是简单地利用decimal来实现小数的精确表示以及运算的话,上面两部分的内容也足够了。如果需要利用decimal来做更多的精度控制,掌握decimal的数值范围,以及超过精度之后选用哪一种四舍五入操作,运算过程中如果产生异常需要自行处理等等操作,就需要对decimal有更深的了解。
更深入的内容在下一篇介绍。