Десятичные суммы в 2 места за деньги в Python 3
Как мне получить десятичные числа, чтобы остаться в 2 местах для представления денег с помощью модуля decimal
?
Я устанавливаю точность и проклинаю почти все остальное, и встречался с ошибкой.
Ответы
Ответ 1
При работе с деньгами вы обычно хотите ограничить точность как можно позже, чтобы такие вещи, как умножение, не суммировали ошибки округления. В 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)
Ответ 2
Принятый ответ в основном правильный, за исключением константы, используемой для операции округления. Вы должны использовать ROUND_HALF_UP
вместо ROUND_05UP
для валютных операций. Согласно docs:
десятичное. 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
Ответ 3
Программисты лживости верят в деньги:
- Денежные значения могут быть сохранены или представлены как плавающая точка.
- Все валюты имеют десятичную точность 2.
- Все идентифицированные валюты ISO 4217 имеют десятичную точность.
- Все валюты определены в ISO 4217.
- Золото не является валютой.
- Моя система никогда не будет обрабатывать неясные валюты с более чем двумя десятичными знаками.
- Значения с плавающей запятой ОК, если денежная стоимость транзакций "мала".
- Система всегда будет обрабатывать одну и ту же валюту (поэтому мы не сохраняем валюту, а только денежную стоимость).
- Сохранение денежных значений, поскольку подписанные длинные целые числа облегчат работу с ними, просто умножьте их на 100 после выполнения всей арифметики.
- Клиенты никогда не будут жаловаться на мои методы округления.
- Когда я конвертирую свое приложение из языка X в язык Y, мне не нужно проверять, является ли поведение округления одинаковым.
- При обмене валюты A на валюту B обменный курс становится неактуальным после транзакции.
Ответ 4
Один из способов решения этой проблемы - хранить денежные значения в центах как целые числа и преобразовывать их только в десятичное представление при печати значений. Это называется арифметикой с фиксированной точкой.
Ответ 5
>>> decimal.getcontext().prec = 2
>>> d = decimal.Decimal('2.40')
>>> d/17
Decimal('0.14')
Вам просто нужно установить точность на 2 (первая строка), и все они будут использовать не более двух знаков после запятой
Просто для сравнения:
>>> 2.4 / 17
0.1411764705882353