每日10行代码122: 编写高质量python代码的方法47:在重视精确度的场合,应该使用 decimal

Python 语言很合适用来编写与数值型数据打交道的代码。Python 的整数类型,可以表达任意长度的值,其双精度浮点数类型,也遵循 IEEE 754 标准。此外,Python 还提供了标准复数类型,用来表示虚数值。然而这引起数值类型,并不能覆盖每一种情况。
例如,要根据通话时长和费率,来计算打电话费。假如用户打了3分42秒,打国际长途,每分钟1.45元,那么通话费是多少呢?

rate = 1.45
seconds = 3*60 + 42
cost = rate*seconds / 60
print(cost)
>>>
5.364999999999999

但是,把计算结果向分位取整之后,却发现,round函数把贫僧右侧 的那些数字完全舍去了。实际上,不足1分钱的部分,是应该按1分钱收取的,我们希望 round 函数把该值上调为 5.37, 而不是下调为 5.36.

print(round(cost,2))
>>>
5.36

假设有一种计费规则是这样的,每分钟的费用是5分钱,最低计费时间是1秒,那么5秒钟的费用是:

rate = 0.05
seconds = 5
cost = rate*seconds / 60
print(cost)
print(round(cost,2))
>>>
0.004166666666666667
0.0

由于计算出来的数值很少,所以round函数返回的是0,但是运营商一般制定的规则是不足1分钱算1分钱。
在python 内置的 decimal 模块中,有个 Decimal 类,可以解决上面那些问题。该类默认提供28 个小数位,以进行定点(fixed point ) 数学运算。如果有需要,还可以把精度调得更高一些。 Decimal 类解决了 IEEE 754 浮点数所产生的精度 问题,而且开发者还可以更加精准地控制该类的传入行为。
例如,我们可以把刚才的那个程序,用Decimal 类改写。 改写之后,程序会算出精确的结果,而不会像原来那样,只求出近似值。

from decimal import Decimal

rate = Decimal('1.45')
seconds = Decimal('222')    # 3*60 + 42
cost = rate*seconds / Decimal('60')
print(cost)
print(round(cost,2))
>>>
5.365
5.36

结果还是有些不对,不过Decimal 类提供了一个内置的函数 ,它可以按照开发者所要求的精度及舍入方式,来准确地调整数值。

from decimal import Decimal,ROUND_UP

rate = Decimal('1.45')
seconds = Decimal('222')    # 3*60 + 42
cost = rate*seconds / Decimal('60')
print(cost)
print(round(cost,2))

rounded = cost.quantize(Decimal('0.01'), rounding=ROUND_UP)
print(rounded)
>>>
5.365
5.36
5.37

这个quantize 方法,也能对那种时长很短,费用很低的电话,正确地进行计费。我们用Decimal 类来改写之前的那段代码。改写之后 ,计算出来的电话费用,还是不足1分钱:

from decimal import Decimal,ROUND_UP

rate = Decimal('0.05')
seconds = Decimal('5')
cost = rate*seconds / Decimal('60')
print(cost)
>>>
0.004166666666666666666666666667

但是,我们可以在调用quantize方法时,指定合理的舍入方式 ,从而确保该方法能够把不足1分钱的部分,上调为1分钱。

rounded = cost.quantize(Decimal('0.01'), rounding=ROUND_UP)
print(rounded)
>>>
0.01

虽然 Decimal 类很适合执行定点数的运算,但它在精确度方面仍有局限,例如, 1/3 这个数,就只能用近似值来表示。如果要用精度不受限制的方式来表达有理数,那么可以考虑使用 Fraction 类,该类包含在内置的 fractions 模块里。

要点:

  • 对于编程中可能用到的每一种数值 ,我们都可以拿对应的 Python 内置类型,或内置模块中的类表示。
  • Decimal 类非常适合用在那种对精度要求很高,且对舍入行为要求很严的场合,例如,涉及货币计算的场合。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值