Обычное начало использования модуля десятичных чисел начинается с импорта модуля decimal
и просмотр текущего контекста с помощью decimal.getcontext()
и при необходимости, установка новых значений для необходимой точности и округления:
>>> from decimal import * >>> getcontext() Context(prec=28, rounding=ROUND_HALF_EVEN, Emin=-999999, Emax=999999, capitals=1, clamp=0, flags=[], traps=[Overflow, DivisionByZero, InvalidOperation]) # Установка новой точности >>> getcontext().prec = 7
Десятичные числа модуля decimal
могут быть построены из целых чисел, строк, чисел с плавающей точкой или кортежей. Конструкция из целого числа или числа с плавающей точкой выполняет точное преобразование значения этого целого числа или числа с плавающей точкой. Десятичные числа включают в себя специальные значения, такие как NaN
, что означает "Не число", положительная и отрицательная бесконечность Infinity
и -0:
>>> from decimal import * >>> getcontext().prec = 28 >>> Decimal(10) # Decimal('10') >>> Decimal('3.14') # Decimal('3.14') >>> Decimal(3.14) # Decimal('3.140000000000000124344978758017532527446746826171875') >>> Decimal((0, (3, 1, 4), -2)) # Decimal('3.14') >>> Decimal(str(2.0 ** 0.5)) # Decimal('1.4142135623730951') >>> Decimal(2) ** Decimal('0.5') # Decimal('1.414213562373095048801688724') >>> Decimal('NaN') # Decimal('NaN') >>> Decimal('-Infinity') # Decimal('-Infinity')
Если сигнал decimal.FloatOperation
перехватывает случайное смешивание десятичных чисел и значений с плавающей запятой в конструкторах или в сравнениях, то вызывается исключение:
>>> c = getcontext() >>> c.traps[FloatOperation] = True >>> Decimal(3.14) # Traceback (most recent call last): # File "<stdin>", line 1, in <module> # decimal.FloatOperation: [<class 'decimal.FloatOperation'>] >>> Decimal('3.5') < 3.7 # Traceback (most recent call last): # File "<stdin>", line 1, in <module> # decimal.FloatOperation: [<class 'decimal.FloatOperation'>] >>> Decimal('3.5') == 3.5 # True
Значимость нового десятичного числа определяется исключительно количеством введенных цифр. Точность контекста и округление вступают в игру только во время арифметических операций.
>>> getcontext().prec = 6 >>> Decimal('3.0') # Decimal('3.0') >>> Decimal('3.1415926535') # Decimal('3.1415926535') >>> Decimal('3.1415926535') + Decimal('2.7182818285') # Decimal('5.85987') >>> getcontext().rounding = ROUND_UP >>> Decimal('3.1415926535') + Decimal('2.7182818285') # Decimal('5.85988')
Если внутренние пределы числа версии языка C превышены, построение десятичного числа вызывает исключение InvalidOperation
:
>>> Decimal("1e9999999999999999999") # Traceback (most recent call last): # File "<stdin>", line 1, in <module> # decimal.InvalidOperation: [<class 'decimal.InvalidOperation'>]
Десятичные дроби хорошо взаимодействуют с большей частью Python.
>>> data = list(map(Decimal, '1.34 1.87 3.45 2.35 1.00 0.03 9.25'.split())) >>> max(data) # Decimal('9.25') >>> min(data) # Decimal('0.03') >>> sorted(data) # [Decimal('0.03'), Decimal('1.00'), # Decimal('1.34'), Decimal('1.87'), # Decimal('2.35'), Decimal('3.45'), # Decimal('9.25')] >>> sum(data) # Decimal('19.29') >>> a, b, c = data[:3] >>> str(a) # '1.34' >>> float(a) # 1.34 >>> round(a, 1) # Decimal('1.3') >>> int(a) # 1 >>> a * 5 # Decimal('6.70') >>> a * b # Decimal('2.5058') >>> c % a # Decimal('0.77')
А некоторые математические функции также доступны для класса decimal.Decimal()
в качестве методов:
>>> getcontext().prec = 28 >>> Decimal(2).sqrt() # Decimal('1.414213562373095048801688724') >>> Decimal(1).exp() # Decimal('2.718281828459045235360287471') >>> Decimal('10').ln() # Decimal('2.302585092994045684017991455') >>> Decimal('10').log10() # Decimal('1')
Метод Decimal.quantize()
округляет число до фиксированной степени. Этот метод полезен для денежных приложений, которые часто округляют результаты до фиксированной точности:
Decimal('7.325').quantize(Decimal('.01'), rounding=ROUND_DOWN) # Decimal('7.32') Decimal('7.325').quantize(Decimal('1.'), rounding=ROUND_UP) # Decimal('8')
Как показано выше, функция decimal.getcontext()
получает доступ к текущему контексту и позволяет изменять настройки. Этот подход отвечает потребностям большинства приложений.
Для более сложной работы может быть полезно создать альтернативные контексты с помощью конструктора decimal.Context()
. Чтобы сделать альтернативу активному контексту, используйте функцию decimal.setcontext()
.
В соответствии со стандартом десятичный модуль decimal
предоставляет два готовых к использованию стандартных контекста, BasicContext
и ExtendedContext
. Первый особенно полезен для отладки:
>>> myothercontext = Context(prec=60, rounding=ROUND_HALF_DOWN) >>> setcontext(myothercontext) >>> Decimal(1) / Decimal(7) # Decimal('0.142857142857142857142857142857142857142857142857142857142857') >>> ExtendedContext # Context(prec=9, rounding=ROUND_HALF_EVEN, Emin=-999999, Emax=999999, capitals=1, clamp=0, flags=[], traps=[]) >>> setcontext(ExtendedContext) >>> Decimal(1) / Decimal(7) # Decimal('0.142857143') >>> Decimal(42) / Decimal(0) # Decimal('Infinity') >>> setcontext(BasicContext) >>> Decimal(42) / Decimal(0) # Traceback (most recent call last): # File "<pyshell#143>", line 1, in -toplevel- # Decimal(42) / Decimal(0) # DivisionByZero: x / 0
Контексты также имеют сигнальные флаги для мониторинга исключительных условий, возникающих во время вычислений. Флаги остаются установленными до явного сброса, поэтому лучше очищать флаги перед каждым набором отслеживаемых вычислений с помощью метода getcontext().clear_flags()
.
>>> setcontext(ExtendedContext) >>> getcontext().clear_flags() >>> Decimal(355) / Decimal(113) # Decimal('3.14159292') >>> getcontext() # Context(prec=9, rounding=ROUND_HALF_EVEN, # Emin=-999999, Emax=999999, capitals=1, # clamp=0, flags=[Inexact, Rounded], traps=[])
Запись флагов показывает, что рациональное приближение к числу Pi
было округлено, цифры за пределами контекста были отброшены и что результат является неточным, т. к. некоторые из отброшенных цифр были ненулевыми.
Отдельные ловушки traps
устанавливаются с помощью словаря в поле ловушек контекста:
>>> getcontext().traps[Rounded] = 1 >>> getcontext() # Context(prec=9, rounding=ROUND_HALF_EVEN, # Emin=-999999, Emax=999999, capitals=1, # clamp=0, flags=[], traps=[Rounded]) >>> Decimal(1)/Decimal(16) # Decimal('0.0625') >>> Decimal(1)/Decimal(17) # Traceback (most recent call last): # File "<stdin>", line 1, in <module> # decimal.Rounded: [<class 'decimal.Rounded'>]
После установки флага Rounded
в активное значение операции Decimal(1)/Decimal(16)
выполнилась без ошибки, потому что результат операции входит в диапазон точности prec
и следовательно не нуждается в округлении. А вот результат операции Decimal(1)/Decimal(17)
должен быть округлен, а значит вызовет ошибку.