【发布时间】:2016-10-01 00:51:23
【问题描述】:
如何使用decimal 模块让我的小数保留在 2 位以表示货币?
我已经设置了精度,并且该死的几乎所有其他东西,但都失败了。
【问题讨论】:
标签: python python-3.x decimal currency
如何使用decimal 模块让我的小数保留在 2 位以表示货币?
我已经设置了精度,并且该死的几乎所有其他东西,但都失败了。
【问题讨论】:
标签: python python-3.x decimal currency
使用金钱时,您通常希望尽可能晚地限制精度,这样乘法等运算就不会聚合舍入误差。在 python 2 和 3 中,您可以 .quantize() 和 Decimal 达到您想要的任何精度:
unit_price = decimal.Decimal('8.0107')
quantity = decimal.Decimal('0.056')
price = unit_price * quantity
cents = decimal.Decimal('.01')
money = price.quantize(cents, decimal.ROUND_HALF_UP)
【讨论】:
除了用于舍入运算的常数外,公认的答案大部分是正确的。您应该使用ROUND_HALF_UP 而不是ROUND_05UP 进行货币操作。根据docs:
十进制。ROUND_HALF_UP
从零开始舍入到最近。
十进制。ROUND_05UP
如果舍入到零后的最后一位数字是 0 或 5,则从零舍入;否则向零舍入。
如果百分位的数字是 5 或 0,则使用 ROUND_05UP 只会四舍五入(对于正数),这对于货币数学是不正确的。
这里有一些例子:
>>> from decimal import Decimal, ROUND_05UP, ROUND_HALF_UP
>>> cents = Decimal('0.01')
>>> Decimal('1.995').quantize(cents, ROUND_HALF_UP)
Decimal('2.00') # Correct
>>> Decimal('1.995').quantize(cents, ROUND_05UP)
Decimal('1.99') # Incorrect
>>> Decimal('1.001').quantize(cents, ROUND_HALF_UP)
Decimal('1.00') # Correct
>>> Decimal('1.001').quantize(cents, ROUND_05UP)
Decimal('1.01') # Incorrect
【讨论】:
程序员相信金钱的谎言:
【讨论】:
解决此问题的一种方法是将 cents 中的货币值存储为整数,并且仅在打印值时转换为十进制表示。这称为fixed point arithmetic。
【讨论】:
>>> decimal.getcontext().prec = 2
>>> d = decimal.Decimal('2.40')
>>> d/17
Decimal('0.14')
您只需将精度设置为 2(第一行),所有内容都将使用不超过 2 个小数位
只是为了比较:
>>> 2.4 / 17
0.1411764705882353
【讨论】:
Decimal的行为,除非你理解可能的结果并且有工具来衡量它(写很多单元测试,单元测试总是好的!)。在应用程序的一端摆弄库的全局设置很容易破坏项目另一部分中看似无关的东西。如果您足够幸运,您还可以更改您使用的第三方模块的行为,而这些模块恰好在内部使用Decimal。
decimal.Decimal('9.99') + decimal.Decimal(0) == 10 并将上下文 prec 设置为 2。