Сообщить об ошибке.

Руководство и примеры использования модуля decimal

Обычное начало использования модуля десятичных чисел начинается с импорта модуля 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) должен быть округлен, а значит вызовет ошибку.