精确的小数-decimal初探

原载于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*bDecimal('-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有更深的了解。

更深入的内容在下一篇介绍。


猜你喜欢

转载自blog.csdn.net/weixin_42407546/article/details/80912420