python decimal 精确计算

经常使用的几个点
1.可以传递给Decimal整型或者字符串参数,但不能是浮点数据,因为浮点数据本身就不准确。需要先将浮点数转化为字符串

传入浮点数 5.55 和 传入字符串 ‘5.55’

from decimal import *
print(Decimal(5.55)*100)
print(Decimal('5.55')*100)
print(Decimal('4.20') + Decimal('2.10'))
x = 4.20
y = 3.10
print(Decimal(str(x)) + Decimal(str(y)))
#运行结果分别是:
#554.9999999999999822364316060
#555.00
#6.30
#7.3

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

from decimal import *
print(Decimal.from_float(22.222))
#运行结果:22.22200000000000130739863379858434200286865234375

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

from decimal import *
getcontext().prec = 5
print(Decimal(1)/Decimal(7))
print(Decimal(1.00)/Decimal(3.0))
#运行结果,五个有效数字:0.14286
#运行结果:0.33333

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

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

from decimal import *
print(Decimal('50.5679').quantize(Decimal('0.00')))
#运行结果四舍五入保留了两位小数:50.57

5.Decimal 结果转化为string

from decimal import *
print(str(Decimal('3.40').quantize(Decimal('0.0'))))
#运行结果:3.4

因为文章是转发过来的,我执行的结果并不是str,还待研究

特别注意:prec为有效数字长度。
如果prec的长度比数字的长度小的话,*100得出的数就不对了

from decimal import *
print(getcontext())  # Context(prec=28, rounding=ROUND_HALF_EVEN, Emin=-999999, Emax=999999, capitals=1, clamp=0, flags=[], traps=[InvalidOperation, DivisionByZero, Overflow])
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

6.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)

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

1)首先给出一组负数的后一位超过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.5556
print(x.quantize(Decimal('1.0000'), ROUND_UP))           # -5.5556
print(x.quantize(Decimal('1.0000'), ROUND_DOWN))         # -5.5555

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

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

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

2)再多对比一组后一位没有超过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_EVEN 和 ROUND_HALF_DOWN:EVEN是quansize的默认设置值,可以通过getcontext()得到,EVEN由于达不到四舍五入所以不进位,DOWN同样也不进位。

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

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

3)正数部分后面数大于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_EVEN 和 ROUND_HALF_DOWN:EVEN是quansize的默认设置值,可以通过getcontext()得到,EVEN由于达到四舍五入所以进位,DOWN同样进位。

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

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

4)正数部分后面数小于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_EVEN 和 ROUND_HALF_DOWN:EVEN是quansize的默认设置值,可以通过getcontext()得到,EVEN由于没有达到四舍五入所以不进位,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就始终不进位。

参考:https://blog.csdn.net/weixin_37989267/article/details/79473706

相关推荐
©️2020 CSDN 皮肤主题: 创作都市 设计师:CSDN官方博客 返回首页