Десятичная фиксированная точность - PullRequest
0 голосов
/ 02 июля 2019

Я хотел бы использовать десятичную дробь в расчетах валют, поэтому я хотел бы работать ровно с двумя числами после запятой. Первоначально я думал, что prec контекста decimal относится к этому свойству, но после нескольких экспериментов я чувствую себя немного смущенным.

Эксперимент № 1:

In [1]: import decimal                                                                                                                      

In [2]: decimal.getcontext().prec = 2                                                                                                       

In [3]: a = decimal.Decimal('159.9')                                                                                                        

In [4]: a                                                                                                                                   
Out[4]: Decimal('159.9')

In [5]: b = decimal.Decimal('200')                                                                                                          

In [6]: b                                                                                                                                   
Out[6]: Decimal('200')

In [7]: b - a                                                                                                                               
Out[7]: Decimal('40')

Эксперимент № 2:

In [8]: decimal.getcontext().prec = 4                                                                                                       

In [9]: a = decimal.Decimal('159.9')                                                                                                        

In [10]: a                                                                                                                                  
Out[10]: Decimal('159.9')

In [11]: b = decimal.Decimal('200')                                                                                                         

In [12]: b                                                                                                                                  
Out[12]: Decimal('200')

In [13]: b - a                                                                                                                              
Out[13]: Decimal('40.1')

Эксперимент № 3: (prec по-прежнему установлен на 4)

In [14]: a = decimal.Decimal('159999.9')                                                                                                    

In [15]: a                                                                                                                                  
Out[15]: Decimal('159999.9')

In [16]: b = decimal.Decimal('200000')                                                                                                      

In [17]: b                                                                                                                                  
Out[17]: Decimal('200000')

In [18]: b - a                                                                                                                              
Out[18]: Decimal('4.000E+4')

Почему это работает как в моих примерах? Как мне работать с десятичными знаками в моем случае (расчеты валюты)?

Ответы [ 2 ]

1 голос
/ 02 июля 2019

Лучший метод, который я нашел, это установить максимум prec и использовать Decimal.quantize для округления вашего результата:

decimal.getcontext().prec=100
a = Decimal('400000.123456789')
b = Decimal('200000.0')
a-b
>>> Decimal('2.0000E+5')
(a-b).quantize(Decimal('0.01'))
>>> Decimal('200000.12')
1 голос
/ 02 июля 2019

Точность задает количество значащих цифр, которое не эквивалентно количеству цифр после десятичной точки.

Так, если у вас есть точность 2, у вас будет две значащие цифры, поэтомучисло с 3 значащими цифрами, такими как 40.1, будет уменьшено до двух верхних значащих цифр, давая 40.

Нет простого способа установить количество цифр после десятичной точки с помощью Decimal.Однако вы можете использовать высокую точность и всегда round свои результаты с точностью до двух десятичных дробей:

>>> from decimal import Decimal, getcontext
>>> getcontext().prec = 60  # use a higher/lower one if needed
>>> Decimal('200') - Decimal('159.9')
Decimal('40.1')
>>> r = Decimal('200') - Decimal('159.9')
>>> round(r, 2)
Decimal('40.10')

Десятичный раздел FAQ также включает в себя аналогичный вопрос и ответ (используя quantize):

Q.В приложении с фиксированной запятой с двумя десятичными знаками некоторые входные данные имеют много мест и должны быть округлены.Другие не должны иметь избыточных цифр и должны быть проверены.Какие методы следует использовать?

A.Метод quantize () округляет до фиксированного числа десятичных разрядов.Если установлена ​​неточная ловушка, она также полезна для проверки:

>>> TWOPLACES = Decimal(10) ** -2       # same as Decimal('0.01')
>>> # Round to two places
>>> Decimal('3.214').quantize(TWOPLACES)
Decimal('3.21')
>>> # Validate that a number does not exceed two places
>>> Decimal('3.21').quantize(TWOPLACES, context=Context(traps=[Inexact]))
Decimal('3.21')
>>> Decimal('3.214').quantize(TWOPLACES, context=Context(traps=[Inexact]))
Traceback (most recent call last):
   ...
Inexact: None

Q.Как только я получу действительные двухместные входные данные, как мне сохранить этот инвариант в приложении?

A.Некоторые операции, такие как сложение, вычитание и умножение на целое число, автоматически сохраняют фиксированную точку.Другие операции, такие как деление и нецелочисленное умножение, изменят количество десятичных разрядов, и после них необходимо выполнить шаг quantize ():

>>> a = Decimal('102.72')           # Initial fixed-point values
>>> b = Decimal('3.17')
>>> a + b                           # Addition preserves fixed-point
Decimal('105.89')
>>> a - b
Decimal('99.55')
>>> a * 42                          # So does integer multiplication
Decimal('4314.24')
>>> (a * b).quantize(TWOPLACES)     # Must quantize non-integer multiplication
Decimal('325.62')
>>> (b / a).quantize(TWOPLACES)     # And quantize division
Decimal('0.03')

При разработке приложений с фиксированной запятой этоудобно определять функции для обработки шага quantize ():

>>> def mul(x, y, fp=TWOPLACES):
...     return (x * y).quantize(fp)
>>> def div(x, y, fp=TWOPLACES):
...     return (x / y).quantize(fp)

>>> mul(a, b)                       # Automatically preserve fixed-point
Decimal('325.62')
>>> div(b, a)
Decimal('0.03')
...