Десятичные знаки до 2 мест для денег в Python 3 - PullRequest
13 голосов
/ 26 сентября 2011

Как мне получить мои десятичные числа в двух местах для представления денег с помощью модуля decimal?

Я установил точность, и, черт возьми, почти все остальное, и столкнулся с ошибкой.

Ответы [ 5 ]

29 голосов
/ 27 сентября 2011

При работе с деньгами обычно требуется максимально ограничить точность, чтобы такие вещи, как умножение, не агрегировали ошибки округления. В Python 2 и 3 вы можете .quantize() a 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)
8 голосов
/ 13 июля 2016

Принятый ответ в основном правильный, за исключением константы, используемой для операции округления. Вы должны использовать ROUND_HALF_UP вместо ROUND_05UP для валютных операций. Согласно документам :

десятичное. ROUND_HALF_UP

Округление до ближайшей связи, уходящей от нуля.

десятичное. ROUND_05UP

Округление от нуля, если последняя цифра после округления до нуля была бы 0 или 5; в противном случае округление до нуля.

Использование ROUND_05UP будет округлять (только для положительных чисел) только в том случае, если число на сотом месте было 5 или 0, что неправильно для математики валют.

Вот несколько примеров:

>>> 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
4 голосов
/ 27 июля 2014

Программисты лжи верят в деньги:

  • Денежные значения могут быть сохранены или представлены в виде плавающей запятой.
  • Все валюты имеют десятичную точность 2.
  • Все валюты, определенные в ISO 4217, имеют десятичную точность.
  • Все валюты определены в ISO 4217.
  • Золото не является валютой.
  • Моей системе никогда не придется обрабатывать неясные валюты с более чем двумя десятичными знаками.
  • Значения с плавающей точкой в ​​порядке, если денежная стоимость транзакций "мала".
  • Система всегда будет обрабатывать одну и ту же валюту (поэтому мы не сохраняем валюту, а только денежную стоимость).
  • Хранение денежных значений в виде длинных целых чисел со знаком облегчит их работу, просто умножьте их на 100 после выполнения всех арифметических операций.
  • Клиенты никогда не будут жаловаться на мои методы округления.
  • Когда я конвертирую свое приложение с языка X на язык Y, мне не нужно проверять, одинаковое ли поведение округления.
  • При обмене валюты A на валюту B обменный курс теряет значение после транзакции.
1 голос
/ 26 сентября 2011

Один из способов решить эту проблему - хранить денежные значения в центах как целые числа и преобразовывать их в десятичное представление только при печати значений. Это называется арифметика с фиксированной точкой .

0 голосов
/ 26 сентября 2011
>>> decimal.getcontext().prec = 2
>>> d = decimal.Decimal('2.40')
>>> d/17
Decimal('0.14')

Вам просто нужно установить точность 2 (первая строка), и все они будут использовать не более 2 десятичных знаков

Только для сравнения:

>>> 2.4 / 17
0.1411764705882353
...