python decimal 精确计算

经常使用的几个点

1.可以传递给Decimal整型或者字符串参数,但不能是浮点数据,因为浮点数据本身就不准确。

传入浮点数 5.55

In [74]: Decimal(5.55)*100

Out[74]: Decimal('554.9999999999999822364316060')


传入字符串 ‘5.55’

In [75]: Decimal('5.55')*100
Out[75]: Decimal('555.00')

2.要从浮点数据转换为Decimal类型

from decimal import *

Decimal.from_float(22.222)

# 结果为Decimal('22.2219999999999995310417943983338773250579833984375')

3.通过设定有效数字,限定结果样式:

from decimal import *

getcontext().prec = 6

Decimal(1)/Decimal(7)

# 结果为Decimal('0.142857'),六个有效数字

4.四舍五入,保留几位小数

from decimal import *

Decimal('50.5679').quantize(Decimal('0.00'))

# 结果为Decimal('50.57'),结果四舍五入保留了两位小数

5.Decimal 结果转化为string

from decimal import *

str(Decimal('3.40').quantize(Decimal('0.0')))

# 结果为'3.40',字符串类型

特别注意  

from decimal import *
print(getcontext())  # Context(prec=28, rounding=ROUND_HALF_EVEN, Emin=-999999, Emax=999999, capitals=1, 。。。。。)
num,num1 = '12355','123.55'
getcontext().prec = len(num) +2
print(Decimal(num1)*100 == Decimal(num))   True
getcontext().prec = 3
# todo 如果prec的长度比数字的长度小的话,*100得出的数就不对了
print(Decimal(num1)*100)                   1.24E+4
print(Decimal(num1))                       123.55
print(Decimal(num1)*100 == Decimal(num))   False
print(Decimal(num))                        12355

decimal模块进行十进制数学计算

python中的decimal模块可以解决上面的烦恼 
decimal模块中,可以通过整数,字符串或原则构建decimal.Decimal对象。如果是浮点数,特别注意因为浮点数本身存在误差,需要先将浮点数转化为字符串。

>>> from decimal import Decimal>>> from decimal import getcontext

>>> Decimal('4.20') + Decimal('2.10')Decimal('6.30')

>>> from decimal import Decimal

>>> from decimal import getcontext

>>> x = 4.20

>>> y = 2.10

>>> z = Decimal(str(x)) + Decimal(str(y))

>>> zDecimal('6.3')

>>> getcontext().prec = 4 #设置精度

>>> Decimal('1.00') /Decimal('3.0')

输出 Decimal('0.3333')

当然精度提升的同时,肯定带来的是性能的损失。在对数据要求特别精确的场合(例如财务结算),这些性能的损失是值得的。但是如果是大规模的科学计算,就需要考虑运行效率了。毕竟原生的float比Decimal对象肯定是要快很多的。

python decimal.quantize()参数rounding的各参数解释与行为

我最开始其实是由于疑惑ROUND_FLOOR和 ROUND_DOWN的表现区别才看了一波文档,但是感觉拉出一票以前没有留意过的东西。

贴一个decimal文档里面的解释:

复制代码
ROUND_CEILING (towards Infinity),
ROUND_DOWN (towards zero),
ROUND_FLOOR (towards -Infinity),
ROUND_HALF_DOWN (to nearest with ties going towards zero),
ROUND_HALF_EVEN (to nearest with ties going to nearest even integer),
ROUND_HALF_UP (to nearest with ties going away from zero), or
ROUND_UP (away from zero).
ROUND_05UP (away from zero if last digit after rounding towards zero would have been 0 or 5; otherwise towards zero)
复制代码

 

直接阅读上面的解释十分抽象,下面我结合例子来解释一下在正负数不同的情况下 他们究竟有着什么样的行为

首先给出一组负数的后一位超过5的数据:

from decimal import *

x = Decimal('-3.333333333') + Decimal('-2.222222222')
print(x)   # -5.555555555
print(x.quantize(Decimal('1.0000'), ROUND_HALF_EVEN))    # -5.5556
print(x.quantize(Decimal('1.0000'), ROUND_HALF_DOWN))    # -5.5556
print(x.quantize(Decimal('1.0000'), ROUND_CEILING))      # -5.5555
print(x.quantize(Decimal('1.0000'), ROUND_FLOOR))        # -5.8599
print(x.quantize(Decimal('1.0000'), ROUND_UP))           # -5.8599
print(x.quantize(Decimal('1.0000'), ROUND_DOWN))         # -5.5555

ROUND_HALF_EVENT 和 ROUND_HALF_DOWN:EVENT是quansize的默认设置值,可以通过getcontext()得到,EVENT四舍五入进了一位,DOWN为接近最近的0进了一位。

ROUND_CEILING 和 ROUND_FLOOR:CEILING超过5没有进位是因为它倾向正无穷,FLOOR为了总是变得更小所以进了一位。

ROUND_UP 和 ROUND_DOWN:UP始终进位,DOWN始终不会进位。。

 

再多对比一组后一位没有超过5的数据:

from decimal import *

x = Decimal('-3.333333333') + Decimal('-1.111111111')
print(x)   # 4.444444444
print(x.quantize(Decimal('1.0000'), ROUND_HALF_EVEN))    # -4.4444
print(x.quantize(Decimal('1.0000'), ROUND_HALF_DOWN))    # -4.4444
print(x.quantize(Decimal('1.0000'), ROUND_CEILING))      # -4.4444
print(x.quantize(Decimal('1.0000'), ROUND_FLOOR))        # -4.4445
print(x.quantize(Decimal('1.0000'), ROUND_UP))           # -4.4445
print(x.quantize(Decimal('1.0000'), ROUND_DOWN))         # -4.4444

ROUND_HALF_EVENT 和 ROUND_HALF_DOWN:EVENT是quansize的默认设置值,可以通过getcontext()得到,EVENT由于达不到四舍五入所以不进位,DOWN同样也不进位。

ROUND_CEILING 和 ROUND_FLOOR:CEILING倾向正无穷不进位,FLOOR即使没有超过5,但是为了总是变得更小进了一位。

ROUND_UP 和 ROUND_DOWN:UP始终进位,DOWN始终不会进位。。

 

正数部分后面数大于5的情况:

from decimal import *

x = Decimal('3.333333333') + Decimal('2.222222222')
print(x)   # 5.555555555
print(x.quantize(Decimal('1.0000'), ROUND_HALF_EVEN))    # 5.5556
print(x.quantize(Decimal('1.0000'), ROUND_HALF_DOWN))    # 5.5556
print(x.quantize(Decimal('1.0000'), ROUND_CEILING))      # 5.5556
print(x.quantize(Decimal('1.0000'), ROUND_FLOOR))        # 5.5555
print(x.quantize(Decimal('1.0000'), ROUND_UP))           # 5.5556
print(x.quantize(Decimal('1.0000'), ROUND_DOWN))         # 5.5555

ROUND_HALF_EVENT 和 ROUND_HALF_DOWN:EVENT是quansize的默认设置值,可以通过getcontext()得到,EVENT由于达到四舍五入所以进位,DOWN同样进位。

ROUND_CEILING 和 ROUND_FLOOR:CEILING正数始终进位,FLOOR在正数则始终不会进位。

ROUND_UP 和 ROUND_DOWN:UP始终进位,DOWN始终不会进位。

 

正数部分后面数小于5的情况:

from decimal import *

x = Decimal('3.333333333') + Decimal('1.111111111')
print(x)   # 4.444444444
print(x.quantize(Decimal('1.0000'), ROUND_HALF_EVEN))    # 4.4444
print(x.quantize(Decimal('1.0000'), ROUND_HALF_DOWN))    # 4.4444
print(x.quantize(Decimal('1.0000'), ROUND_CEILING))      # 4.4445
print(x.quantize(Decimal('1.0000'), ROUND_FLOOR))        # 4.4444
print(x.quantize(Decimal('1.0000'), ROUND_UP))           # 4.4445
print(x.quantize(Decimal('1.0000'), ROUND_DOWN))         # 4.4444

ROUND_HALF_EVENT 和 ROUND_HALF_DOWN:EVENT是quansize的默认设置值,可以通过getcontext()得到,EVENT由于没有达到四舍五入所以不进位,DOWN同样不进位。

ROUND_CEILING 和 ROUND_FLOOR:CEILING正数始终进位,FLOOR在正数则始终不会进位。

ROUND_UP 和 ROUND_DOWN:UP始终进位,DOWN始终不会进位。

 

总结:

其实这里我们通过上面一组例子可以发现,正数的行为非常可预期也非常简单,负数的情况稍复杂,有些函数就是设计为负数在某些情况中使用的。正数中无法重现的ROUND_DOWN和ROUND_FLOOR的区别,ROUND_DOWN是无论后面是否大于5都不会管保持原状,而Floor在正数中的行为也是如此,但是在负数中为了倾向无穷小,所以无论是否大于5,他都会变得更小而进位。反而ROUND_UP和ROUND_DOWN的行为是最可预期的,那就是无论后面数大小,UP就进位,DOWN就始终不进位。


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值