开发者

Decimals to 2 places for money in Python 3

How do I get my decimals to stay at 2 places for representing money using the decimal开发者_Go百科 module?

I've setting the precision, and damn near everything else, and met with failure.


When working with money you usually want to limit precision as late as possible so things like multiplication don't aggregate rounding errors. In python 2 and 3 you can .quantize() a Decimal to any precision you want:

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)


Falsehoods programmers believe about money:

  • Monetary values can be stored or represented as a floating point.
  • All currencies have a decimal precision of 2.
  • All ISO 4217 defined currencies have a decimal precision.
  • All currencies are defined in ISO 4217.
  • Gold is not a currency.
  • My system will never have to handle obscure currencies with more than 2 decimal places.
  • Floating point values are OK if the monetary value of transactions is "small".
  • A system will always handle the same currency (therefore we do not persist the currency, only the monetary value).
  • Storing monetary values as signed long integers will make them easier to work with, just multiply them by 100 after all arithmetic is done.
  • Customers will never complain about my rounding methods.
  • When I convert my application from language X to language Y, I don't have to verify if the rounding behavior is the same.
  • On exchanging currency A for currency B, the exchange rate becomes irrelevant after the transaction.


The accepted answer is mostly correct, except for the constant to use for the rounding operation. You should use ROUND_HALF_UP instead of ROUND_05UP for currency operations. According to the docs:

decimal.ROUND_HALF_UP

    Round to nearest with ties going away from zero.

decimal.ROUND_05UP

    Round away from zero if last digit after rounding towards zero would have been 0 or 5; otherwise round towards zero.

Using ROUND_05UP would only round up (for positive numbers) if the number in the hundredths place was a 5 or 0, which isn't correct for currency math.

Here are some examples:

>>> 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


One way to solve this is to store money values in cents as integers, and only convert to decimal representation when printing values. This is called fixed point arithmetic.


>>> decimal.getcontext().prec = 2
>>> d = decimal.Decimal('2.40')
>>> d/17
Decimal('0.14')

You just have to set the precision to 2 (the first line) and them everything will use no more than 2 decimal places

Just for comparison:

>>> 2.4 / 17
0.1411764705882353
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜