decimal — Десятичная арифметика с фиксированной и плавающей точкой

Исходный код: Lib/decimal.py


Модуль decimal предоставляет поддержку быстрой и корректно округляемой десятичной арифметики с плавающей точкой. Это предлагает несколько преимуществ перед типом данных float:

  • Десятичная «основана на модели с плавающей точкой, которая была разработана для людей и обязательно включает главный руководящий принцип - компьютеры должны обеспечивать арифметику, которая работает так же, как арифметика, которую люди учат в школе». - отрывок из десятичной арифметической спецификации.

  • Десятичные числа могут быть представлены точно. Напротив, такие числа, как 1.1 и 2.2, не имеют точных представлений в двоичной плавающей точке. Конечные пользователи обычно не ожидают, что 1.1 + 2.2 будет отображаться так же 3.3000000000000003, как и с двоичной плавающей точкой.

  • Точность переносится в арифметику. В десятичной плавающей точке 0.1 + 0.1 + 0.1 - 0.3 точно равен нулю. В двоичной плавающей точке результат равен 5.5511151231257827e-017. Хотя различия близки к нулю, они препятствуют надежному тестированию на равенство и могут накапливать различия. По этой причине в приложениях учета, имеющих инварианты строгого равенства, предпочтительно использовать десятичное число.

  • Модуль decimal включает понятие значительных мест, так что 1.30 + 1.20 является 2.50. Конечный ноль сохраняется для указания значимости. Это обычное представление для денежного применения. Для умножения в подходе «школьная книга» используются все цифры в множителях. Для сущность, 1.3 * 1.2 дает 1.56, в то время как 1.30 * 1.20 дает 1.5600.

  • В отличие от аппаратных двоичных модулей с плавающей точкой, десятичный модуль имеет изменяемую пользователем точность (значение по умолчанию - 28 разрядов), которая может быть настолько велика, насколько это необходимо для данной задачи:

    >>> from decimal import *
    >>> getcontext().prec = 6
    >>> Decimal(1) / Decimal(7)
    Decimal('0.142857')
    >>> getcontext().prec = 28
    >>> Decimal(1) / Decimal(7)
    Decimal('0.1428571428571428571428571429')
    
  • Как двоичная, так и десятичная плавающая точка реализованы в терминах опубликованных стандартов. В то время как встроенный тип float предоставляет лишь скромную часть своих возможностей, десятичный модуль предоставляет все необходимые части стандарта. При необходимости программист полностью контролирует округление и обработку сигналов. Это включает возможность применения точной арифметики с помощью исключений для блокирования любых неточных операций.

  • Модуль decimal был разработан для поддержки «без предубеждений, как точной неокругляемой десятичной арифметики (иногда называемой арифметикой с фиксированной точкой), так и округляемой арифметики с плавающей точкой». - отрывок из спецификации десятичной арифметически.

Конструкция модуля сосредоточена вокруг трех понятий: десятичное число, арифметический контекст и сигналы.

Десятичное число является неизменяемым. У него есть знак, цифры коэффициента и степень. Для сохранения значимости цифры коэффициента не усекают конечные нули. Десятичные разряды также включают специальные значения, такие как Infinity, -Infinity и NaN. Стандарт также отличает -0 от +0.

Арифметический контекст - точность определяемая средой, правила округления, пределы экспоненты, флаги, указывающие на результаты операций и инструменты реализации ловушки, которые определяют, рассматривают ли сигналы как исключения. Опции округления включают ROUND_CEILING, ROUND_DOWN, ROUND_FLOOR, ROUND_HALF_DOWN, ROUND_HALF_EVEN, ROUND_HALF_UP, ROUND_UP и ROUND_05UP.

Сигналы представляют собой группы исключительных условий, возникающих в процессе вычисления. В зависимости от потребностей приложения сигналы могут игнорироваться, рассматриваться как информационные или рассматриваться как исключения. Сигналы в decimal модуле: Clamped, InvalidOperation, DivisionByZero, Inexact, Rounded, Subnormal, Overflow, Underflow и FloatOperation.

Для каждого сигнала имеется флаг и активатор ловушки. При обнаружении сигнала его флаг устанавливается в единицу, тогда, если активатор ловушки установлен в единицу, поднимается исключение. Флаги являются липкими, поэтому пользователю необходимо сбросить их перед мониторингом вычислений.

См.также

Учебник для быстрого старта

Обычное начало использования десятичных чисел начинается с импортирования модуля, рассматривая текущий контекст с 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       # Установить новую точность

Десятичные сущности могут быть построены из целых чисел, строк, плавающих или кортежей. Построение из целого числа или числа с плавающей точкой выполняет точное преобразование значение этого целого числа или числа с плавающей точкой. Десятичные числа включают специальные значения, такие как NaN, что означает «Не число», положительное и отрицательное Infinity и -0:

>>> 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')

Если сигнал 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

Добавлено в версии 3.3.

Значимость нового десятичного знака определяется исключительно количеством введенных цифр. Точность контекста и округление вступают в действие только во время арифметических операций.

>>> 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'>]

Изменено в версии 3.3.

Децималы хорошо взаимодействуют с большей частью остального 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:

>>> 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')

Метод quantize() округляет число до фиксированной степени. Этот метод полезен для монетарных приложений, которые часто округляют результаты до фиксированного количества мест:

>>> Decimal('7.325').quantize(Decimal('.01'), rounding=ROUND_DOWN)
Decimal('7.32')
>>> Decimal('7.325').quantize(Decimal('1.'), rounding=ROUND_UP)
Decimal('8')

Как показано выше, функция getcontext() осуществляет доступ к текущему контексту и позволяет изменять настройки. Этот подход отвечает потребностям большинства приложений.

Для более продвинутой работы может быть полезно создать альтернативные контексты с помощью конструктора Context(). Чтобы сделать альтернативу активной, используйте функцию 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

Контексты также имеют сигнальные флаги для мониторинга исключительных условий, встречающихся во время вычислений. Флаги остаются установленными до явного сброса, поэтому лучше очистить флаги перед каждым набором контролируемых вычислений с помощью метода 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=[])

Запись flags показывает, что рациональное приближение к Pi было округлено (цифры сверх точности контекст были выброшены) и что результат неточен (некоторые отброшенные цифры были ненулевыми).

Отдельные ловушки устанавливаются с помощью словаря в поле traps контекста:

>>> setcontext(ExtendedContext)
>>> Decimal(1) / Decimal(0)
Decimal('Infinity')
>>> getcontext().traps[DivisionByZero] = 1
>>> Decimal(1) / Decimal(0)
Traceback (most recent call last):
  File "<pyshell#112>", line 1, in -toplevel-
    Decimal(1) / Decimal(0)
DivisionByZero: x / 0

Большинство программ регулирует текущий контекст только однажды, в начале программы. И во многих приложениях данные преобразуются в Decimal с одним приведением внутри цикла. С набором контекст и созданными десятичными числами, большая часть программы управляет данными не иначе, чем с другими числовыми типами Python.

Объекты Decimal

class decimal.Decimal(value="0", context=None)

Создать новый объект Decimal на основе value.

value может быть целым числом, строка, кортежем, float или другим объектом Decimal. Если не передан value, возвращается Decimal('0'). Если value является строкой, она должна соответствовать десятичному числовому синтаксису строки после удаления начальных и конечных символов пробела, а также знаков подчеркивания:

sign           ::=  '+' | '-'
digit          ::=  '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9'
indicator      ::=  'e' | 'E'
digits         ::=  digit [digit]...
decimal-part   ::=  digits '.' [digits] | ['.'] digits
exponent-part  ::=  indicator [sign] digits
infinity       ::=  'Infinity' | 'Inf'
nan            ::=  'NaN' [digits] | 'sNaN' [digits]
numeric-value  ::=  decimal-part [exponent-part] | infinity
numeric-string ::=  [sign] numeric-value | [sign] nan

Другие десятичные цифры Юникода также разрешены там, где digit отображается выше. К ним относятся десятичные цифры из различных других алфавитов (например, арабско-индские и деванагарские цифры) вместе с полноширотными цифрами '\uff10' - '\uff19'.

Если value является tuple, у него должны быть три компонента, знак (0 для положительного или 1 для отрицательного), tuple цифр и целое число. Например, Decimal((0, (1, 4, 1, 4), -3)) возвращает Decimal('1.414').

Если value является float, двоичный значение с плавающей точкой без потерь преобразуется в его точный десятичный эквивалент. Это преобразование часто требует 53 или более цифр точности. Например, Decimal(float('1.1')) преобразует в Decimal('1.100000000000000088817841970012523233890533447265625').

Точность context не влияет на количество сохраняемых цифр. Это определяется исключительно количеством цифр в value. Например, Decimal('3.00000') записывает все пять нулей, даже если точность контекста равна только трем.

Цель аргумента context - определить, что делать, если value является неправильной строкой. Если контекст ловит InvalidOperation, возникает исключение; иначе конструктор возвращает новое десятичное число с значение NaN.

После построения объекты Decimal являются неизменяемыми.

Изменено в версии 3.2: Теперь аргументу конструктора разрешено быть float сущностью.

Изменено в версии 3.3: float аргументы вызывают исключение, если задана ловушка FloatOperation. По умолчанию ловушка выключена.

Изменено в версии 3.6: Для группировки допускаются подчеркивания, как и для литералов с интегральной и плавающей точкой в коде.

Decimal объекты с плавающей точкой содержат много общих свойств с другими встроенными числовыми типами, такими как float и int. Применяются все обычные математические операции и специальные методы. Также, decimal объекты могут быть скопированы, pickled, напечатаны, используемый как ключи словаря, используемый как элементы множества, сравнение, сортировка, и принуждение к другому типу (такому как float или int).

Существуют небольшие различия между арифметикой десятичных объектов и арифметикой целых чисел и плавающих. Когда оператор остатка % применяется к десятичным объектам, знак результата является знаком делимо, а не знаком делителя:

>>> (-7) % 4
1
>>> Decimal(-7) % Decimal(4)
Decimal('-3')

Оператор целочисленного деления // ведет себя аналогично, возвращая целую часть истинного частного (усечение к нулю), а не своего нижнего предела, чтобы сохранить обычное тождество x == (x // y) * y + x % y:

>>> -7 // 4
-2
>>> Decimal(-7) // Decimal(4)
Decimal('-1')

Операторы % и // реализуют операции remainder и divide-integer (соответственно), как описано в описании.

Decimal объекты в арифметических операциях обычно нельзя сочетать с флоатами или сущности fractions.Fraction: попытка добавить Decimal к float, например, поднимет TypeError. Однако возможно использовать операторов сравнения Python, чтобы сравнить Decimal сущность x с другим номером y. Это позволяет избежать путаницы результатов при проведении сравнений равенства между числами различных типов.

Изменено в версии 3.2: Сравнения смешанного типа между Decimal сущности и другими числовыми типами теперь полностью поддерживается.

Помимо стандартных числовых свойств, decimal объекты с плавающей точкой также имеют ряд специализированных методов:

adjusted()

Возвращает скорректированную степень после смещения крайних правых цифр коэффициента до тех пор, пока не останется только ведущая цифра: Decimal('321e+5').adjusted() возвращает семь. Используется для определения положения наиболее значимой цифры относительно десятичной точки.

as_integer_ratio()

Возвращает пару (n, d) целых чисел, которые представляют данную Decimal сущность как дробь, с отрицательным числителем и с положительным знаменателем:

>>> Decimal('-3.14').as_integer_ratio()
(-157, 50)

Преобразование является точным. Поднимет OverflowError для бесконечностей и CreateError для NaN.

Добавлено в версии 3.6.

as_tuple()

Возвращает именованный кортеж представление числа: DecimalTuple(sign, digits, exponent).

canonical()

Возвращает каноническую кодировку аргумента. В настоящее время кодировка Decimal сущность всегда каноническая, таким образом, эта операция возвращает свой неизменный аргумент.

compare(other, context=None)

Сравнение значений двух десятичных сущности. compare() возвращает десятичную сущность, и если любой из операндов является NaN, то результатом будет NaN:

a or b is a NaN  ==> Decimal('NaN')
a < b            ==> Decimal('-1')
a == b           ==> Decimal('0')
a > b            ==> Decimal('1')
compare_signal(other, context=None)

Эта операция идентична методу compare(), за исключением сигнала NaNs. То есть, если ни один из операндов не является сигнальным NaN, то любой тихий операнд NaN рассматривается как сигнальный NaN.

compare_total(other, context=None)

Сравнение двух операнд, используя их абстрактное представление, а не числовые значение. Аналогично методу compare(), но результат даёт общий порядок на Decimal сущности. Два Decimal сущности с одним и тем же числовым значение, но разные представления сравниваются неравнозначно в этом порядке:

>>> Decimal('12.0').compare_total(Decimal('12'))
Decimal('-1')

В общий порядок также включены тихие и сигнальные NaN. Результат этой функции является Decimal('0'), если оба операнда имеют одинаковое представление, Decimal('-1') если первый операнд ниже в общем порядке, чем второй, и Decimal('1') если первый операнд выше в общем порядке, чем второй операнд. Подробные сведения об общем порядке см. в спецификации.

Эта операция не зависит от контекста и является тихой: флаги не изменяются и округление не выполняется. В качестве исключения, версия на языке C может возродить операцию CancelOperation, если второй операнд не может быть преобразован точно.

compare_total_mag(other, context=None)

Сравнение двух операндов, используя их абстрактное представление, а не их значение, как в compare_total(), но игнорируя знак каждого операнда. x.compare_total_mag(y) эквивалентно x.copy_abs().compare_total(y.copy_abs()).

Эта операция не зависит от контекста и является тихой: флаги не изменяются и округление не выполняется. В качестве исключения, версия на языке C может возродить операцию CancelOperation, если второй операнд не может быть преобразован точно.

conjugate()

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

copy_abs()

Возвращает абсолютное значение аргумента. Эта операция не зависит от контекста и является тихой: флаги не изменяются и округление не выполняется.

copy_negate()

Возвращает отрицание аргумента. Эта операция не зависит от контекста и является тихой: флаги не изменяются и округление не выполняется.

copy_sign(other, context=None)

Возвращает копию первого операнда со знаком, равным знаку второго операнда. Например:

>>> Decimal('2.3').copy_sign(Decimal('-1.5'))
Decimal('-2.3')

Эта операция не зависит от контекста и является тихой: флаги не изменяются и округление не выполняется. В качестве исключения, версия на языке C может возродить операцию CancelOperation, если второй операнд не может быть преобразован точно.

exp(context=None)

Возвращает значение (естественной) экспоненциальной функции e**x при заданном числе. Результат правильно округляется с использованием режима округления ROUND_HALF_EVEN.

>>> Decimal(1).exp()
Decimal('2.718281828459045235360287471')
>>> Decimal(321).exp()
Decimal('2.561702493119680037517373933E+139')
from_float(f)

Метод класса, преобразующий плавающую точку в десятичное число.

Примечание Decimal.from_float(0.1) не совпадает с Decimal(„0.1“). Поскольку 0.1 не является точно представимым в двоичной плавающей точке, значение сохраняется как ближайший представимый значение, который является 0x1.999999999999ap-4. Этот эквивалент значение в десятичном формате равен 0.1000000000000000055511151231257827021181583404541015625.

Примечание

Начиная с Python 3.2, Decimal сущность также может быть построена непосредственно из float.

>>> Decimal.from_float(0.1)
Decimal('0.1000000000000000055511151231257827021181583404541015625')
>>> Decimal.from_float(float('nan'))
Decimal('NaN')
>>> Decimal.from_float(float('inf'))
Decimal('Infinity')
>>> Decimal.from_float(float('-inf'))
Decimal('-Infinity')

Добавлено в версии 3.1.

fma(other, third, context=None)

Плавленное умножение-сложение. Возвращает self*other+third без округления промежуточного продукта self*other.

>>> Decimal(2).fma(3, 5)
Decimal('11')
is_canonical()

Возвращает True если аргумент является каноническим и False иначе. В настоящее время Decimal сущность всегда канонична, поэтому эта операция всегда возвращает True.

is_finite()

Возвращает True если аргумент является конечным числом, и False если аргумент является бесконечностью или NaN.

is_infinite()

Возвращает True если аргумент является либо положительным, либо отрицательным бесконечностью и False иначе.

is_nan()

Возвращает True если аргумент является (тихим или сигнализирующим) NaN и False иначе.

is_normal(context=None)

Возвращает True, если аргумент - конечное обычное число. Возвращает False если аргумент равен нулю, субнормали, бесконечности или NaN.

is_qnan()

Возвращает True если аргумент является тихим NaN, и False иначе.

is_signed()

Возвращает True если аргумент имеет отрицательный знак и False иначе. Обратите внимание, что нули и NaN могут нести знаки.

is_snan()

Возвращает True, если аргумент является сигнальным NaN и False иначе.

is_subnormal(context=None)

Возвращает True, если аргумент субнормальный и False иначе.

is_zero()

Возвращает True если аргумент является (положительным или отрицательным) нулем и False иначе.

ln(context=None)

Возвращает натуральный (по основанию е) логарифм операнда. Результат правильно округляется с использованием режима округления ROUND_HALF_EVEN.

log10(context=None)

Возвращает логарифм по основанию 10 операнда. Результат правильно округляется с использованием режима округления ROUND_HALF_EVEN.

logb(context=None)

Для ненулевого числа возвращает скорректированную экспоненту своего операнда как Decimal сущность. Если операнд равен нулю, то Decimal('-Infinity') возвращенныйand поднимается флаг DivisionByZero. Если операнд является бесконечностью, то возвращается Decimal('Infinity').

logical_and(other, context=None)

logical_and() - логическая операция, которая занимает два логические операнды (см. Логические операнды). В результате получается цифра и двух операндов.

logical_invert(context=None)

logical_invert() является логической операцией. Результатом является инверсия операнда по цифрам.

logical_or(other, context=None)

logical_or() - логическая операция, которая берет два логических операнда (см. Логические операнды). В результате получается цифра или два операнда.

logical_xor(other, context=None)

logical_xor() - логическая операция, которая занимает два логических операнда (см. Логические операнды). Результатом является исключительная цифра или два операнда.

max(other, context=None)

Подобно max(self, other) за исключением того, что правило округления контекста применяется прежде, чем возвратиться и те NaN значения либо сигнализируются, либо игнорируются (в зависимости от контекста сигнализируюся или замалчиваются).

max_mag(other, context=None)

Аналогично методу max(), но сравнение выполняется с использованием абсолютной значений операндов.

min(other, context=None)

Подобно min(self, other) за исключением того, что правило округления контекста применяется прежде, чем возвратиться и те NaN значения либо сигнализируются, либо игнорируются (в зависимости от контекста сигнализируюся или замалчиваются).

min_mag(other, context=None)

Аналогично методу min(), но сравнение выполняется с использованием абсолютной значения операндов.

next_minus(context=None)

Возвращает большое число, представляемое в данном контекст (или в текущем контексте потока, если не задан контекст), которое меньше заданного операнда.

next_plus(context=None)

Возвращает наименьшее число, представляемое в данном контекст (или в текущем контексте потока, если не задан контекст), которое больше заданного операнда.

next_toward(other, context=None)

Если два операнда неравны, возвращает число, ближайшее к первому операнду в направлении второго операнда. Если оба операнда численно равны, то возвращает a копия первого операнда со знаком, установленным таким же, как знак второго операнда.

normalize(context=None)

Нормализировать число путем удаления крайних правых конечных нулей и преобразования любого результата, равного Decimal('0'), в Decimal('0e0'). Используется для получения канонических значения для атрибуты класса эквивалентности. Например, Decimal('32.100') и Decimal('0.321000e+2') нормализуются до эквивалентного значение Decimal('32.1').

number_class(context=None)

Возвращает строку описание class операнда. возвращенный значение - один из следующих десяти строк.

  • "-Infinity", что указывает на отрицательную бесконечность операнда.
  • "-Normal", указывающий, что операнд является отрицательным нормальным числом.
  • "-Subnormal", указывающий, что операнд является отрицательным и субнормальным.
  • "-Zero", указывающий, что операнд является отрицательным нулем.
  • "+Zero", указывающий, что операнд является положительным нулем.
  • "+Subnormal", указывающий, что операнд является положительным и субнормальным.
  • "+Normal", указывающий, что операнд является положительным нормальным числом.
  • "+Infinity", что указывает на положительную бесконечность операнда.
  • "NaN", указывающий, что операнд является тихим NaN (не число).
  • "sNaN", указывающий, что операнд является сигнальным NaN.
quantize(exp, rounding=None, context=None)

Возвращает значение равному первому операнду после округления и содержит степень второго операнда.

>>> Decimal('1.41421356').quantize(Decimal('1.000'))
Decimal('1.414')

В отличие от других операций, если длина коэффициента после операции квантования будет больше, чем точность, то сигнализируется InvalidOperation. Это гарантирует, что, если не существует условия ошибки, квантованный экспонент всегда равен показателю правого операнда.

Кроме того, в отличие от других операций, квантование никогда не сигнализирует о Underflow, даже если результат является субнормальным и неточным.

Если степень второго операнда больше степени первого операнда, может потребоваться округление. В этом случае режим округления определяется аргументом rounding, если он задан, иначе заданным аргументом context; если ни один из аргументов не задан, используется режим округления текущего контекстного потока.

Возвращается ошибка всякий раз, когда, получающаяся экспонента больше, чем Emax или меньше, чем Etiny.

radix()

Возвращает Decimal(10), радикс (основание), в котором класс Decimal выполняет всю арифметику. Включено для совместимости со спецификацией.

remainder_near(other, context=None)

Возвращает остаток от деления self на other. Это отличается от self % other тем, что знак остатка выбран таким образом, чтобы минимизировать его абсолютную значение. Точнее, возвращает значение есть self - n * other где n - целое число, ближайшее к точному значение self / other, и если два целых одинаково близки, то выбирается чётное.

Если результат равен нулю, то его знак будет знаком self.

>>> Decimal(18).remainder_near(Decimal(10))
Decimal('-2')
>>> Decimal(25).remainder_near(Decimal(10))
Decimal('5')
>>> Decimal(35).remainder_near(Decimal(10))
Decimal('-5')
rotate(other, context=None)

Возвращает результат поворота цифр первого операнда на величину, заданную вторым операндом. Второй операнд должен быть целым числом в диапазоне - точности через точность. Абсолютная значение второго операнда даёт количество мест для поворота. Если второй операнд положительный, то вращение влево; в противном случае поворот осуществляется вправо. Коэффициент первого операнда при необходимости заполняется нулями до точности длины. Знак и экспонента первого операнда неизменны.

same_quantum(other, context=None)

Проверка, имеет ли self и другой ту же экспоненту или являются ли оба NaN.

Эта операция не зависит от контекста и является тихой: флаги не изменяются и округление не выполняется. В качестве исключения, версия на языке C может возродить операцию CancelOperation, если второй операнд не может быть преобразован точно.

scaleb(other, context=None)

Возвращает первый операнд с экспонентой, регулируемый вторым. Эквивалентно, возвращает первый операнд, умноженный на 10**other. Второй операнд должен быть целым числом.

shift(other, context=None)

Возвращает результат сдвига цифр первого операнда на величину, заданную вторым операндом. Второй операнд должен быть целым числом в диапазоне - точности. Абсолютная значение второго операнда даёт количество мест для сдвига. Если второй операнд положительный, то сдвиг влево; в противном случае смещение происходит вправо. Цифры, сдвинутые в коэффициент, являются нулями. Знак и экспонента первого операнда неизменны.

sqrt(context=None)

Возвращает квадратный корень аргумента до полной точности.

to_eng_string(context=None)

Преобразовать в строку, используя инженерную нотацию, если требуется степень.

Инженерная нотация содержит степень, кратную 3. Это может оставить до 3 цифр слева от десятичного знака и может потребовать добавления одного или двух конечных нулей.

Например, этот метод преобразует Decimal('123E+1') в Decimal('1.23E+3').

to_integral(rounding=None, context=None)

Идентичен методу to_integral_value(). Имя to_integral сохранено для совместимости со старыми версиями.

to_integral_exact(rounding=None, context=None)

Округление к ближайшему целому числу, сигнализируя о Inexact или Rounded как соответствующих, если округление происходит. Режим округления определяется параметром rounding и еще переданным context. Если никакой параметр не передан тогда, тогда используется режим округления текущего контекста.

to_integral_value(rounding=None, context=None)

Округление до ближайшего целого числа без сигнализации Inexact или Rounded. Если указано, применяется rounding; в противном случае используется метод округления либо в поставляемом context, либо в текущем контексте.

Логические операнды

Методы logical_and(), logical_invert(), logical_or() и logical_xor() ожидают, что их аргументы будут логическими операндами. A логический операнд - это Decimal сущность, показатель и знак которого равны нулю, а все цифры равны 0 или 1.

Объекты контекста

Контексты - это среды для арифметических операций. Они управляют точностью, устанавливают правила округления, определяют, какие сигналы рассматриваются как исключения и ограничивают диапазон для экспонент.

Каждый поток содержит свой собственный текущий контекст, доступ к которому или изменение которого осуществляется с помощью функций getcontext() и setcontext():

decimal.getcontext()

Возвращает текущее контекст для активного потока.

decimal.setcontext(c)

Установить текущий контекст для активного потока в c.

Вы можете также использовать with инструкция и функцию localcontext(), чтобы временно изменить активный контекст.

decimal.localcontext(ctx=None)

Диспетчер возвращает контекст, который установит текущее контекст для активного поток в копию ctx при входе в with-инструкция и восстановит предыдущее контекст при выходе из with-инструкции. Если контекст не определен, используется копия текущего контекста.

Например, следующий код устанавливает текущую десятичную точность в 42 места, выполняет вычисление и затем автоматически восстанавливает предыдущий контекст:

from decimal import localcontext

with localcontext() as ctx:
    ctx.prec = 42   # Выполнить высокую точность вычислений
    s = calculate_something()
s = +s  # Округлить конечный результат до точности по умолчанию

Новые контексты также могут быть созданы с помощью конструктора Context, описанного ниже. Кроме того, модуль предоставляет три предварительно созданных контекста:

class decimal.BasicContext

Стандартный контекст, определяемый общей десятичной арифметической спецификацией. Точность устанавливается равной девяти. Округление установлено в ROUND_HALF_UP. Все флаги очищены. Все ловушки включены (рассматриваются как исключения), кроме Inexact, Rounded и Subnormal.

Поскольку многие ловушки включены, этот контекст полезен для отладки.

class decimal.ExtendedContext

Стандартный контекст, определенный общей десятичной арифметической спецификацией. Точность устанавливается равной девяти. Округление установлено в ROUND_HALF_EVEN. Все флаги очищены. Ловушки не включены (чтобы исключения не возникали во время вычислений).

Поскольку ловушки отключены, этот контекст полезен для приложений, которые предпочитают получить в результате значение NaN или Infinity вместо того, чтобы поднять исключения. Это позволяет приложению завершить запуск в присутствии условий, которые в противном случае остановят программу.

class decimal.DefaultContext

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

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

В однопоточных средах предпочтительно вообще не использовать этот контекст. Вместо этого просто создайте контексты явно, как описано ниже.

Значения по умолчанию - prec= :const:28, rounding= :const:ROUND_HALF_EVEN, и включить ловушки для Overflow, InvalidOperation и DivisionByZero.

В дополнение к трем предоставленным контекстам можно создавать новые контексты с помощью конструктора Context.

class decimal.Context(prec=None, rounding=None, Emin=None, Emax=None, capitals=None, clamp=None, flags=None, traps=None)

Создание нового контекста. Если поле не указано или является None, значения по умолчанию копируются из DefaultContext. Если поле flags не указано или имеет значение None, все флаги очищаются.

prec - целое число в диапазоне [1, MAX_PREC], которое задает точность для арифметических операций в контекста.

Параметр rounding является одной из констант, перечисленных в разделе Режимы округления.

В полях traps и flags перечислены любые сигналы, которые должны быть установлены. Как правило, новые контексты должны только устанавливать ловушки и оставлять флаги чистыми.

Поля Emin и Emax являются целыми числами, задающими внешние пределы, допустимые для экспонент. Emin должен быть в диапазоне [MIN_EMIN, 0], Emax в диапазоне [0, MAX_EMAX].

Поле capitals должно быть 0 или 1 (по умолчанию). Если установлено значение 1, экспоненты печатаются с помощью прописной E; иначе строчной e - используется: Decimal('6.02e+23').

Поле clamp содержит значение 0 (по умолчанию) или 1. Если установлено в 1, экспонента e Decimal сущность представляется в этом контексте строго ограничен диапазоном Emin - prec + 1 <= e <= Emax - prec + 1. Если clamp 0, то держится более слабое условие: скорректированная экспонента Decimal сущность составляет максимум Emax. Когда clamp равно 1, большое нормальное число, где это возможно, будет иметь уменьшенную степень и соответствующее количество нулей, добавленных к его коэффициенту, чтобы соответствовать ограничениям экспоненты; это сохраняет значение числа, но теряет информацию о значимых конечных нулях. Например:

>>> Context(prec=6, Emax=999, clamp=1).create_decimal('1.23e999')
Decimal('1.23000E+999')

clamp значение 1 допускает совместимость с форматами десятичного обмена фиксированной ширины, заданными в IEEE 754.

Класс Context определяет несколько методов общего назначения, а также большое количество методов для выполнения арифметики непосредственно в данном контекста. Кроме того, для каждого из описанных выше способов Decimal (за исключением методов adjusted() и as_tuple()) существует соответствующий метод Context. Например, для Context сущности C и Decimal сущности x, C.exp(x) эквивалентно x.exp(context=C). Каждый метод Context принимает целое число Python (сущность int) где угодно, что принять Decimal сущность.

clear_flags()

Сбрасывает все флаги в 0.

clear_traps()

Сбрасывает все ловушки в 0.

Добавлено в версии 3.3.

copy()

Возвращает дубликат контекста.

copy_decimal(num)

Возвращает из копии Decimal сущности в num.

create_decimal(num)

Создает новую Decimal сущность из num, но с использованием self в качестве контекста. В отличие от конструктора Decimal, к преобразованию применяются точность контекста, метод округления, флаги и ловушки.

Это полезно, поскольку константы часто задаются с большей точностью, чем требуется приложению. Другое преимущество состоит в том, что округление сразу же устраняет непреднамеренные эффекты от цифр, превышающих текущую точность. В следующем примере использование неокругленных входных данных означает, что добавление нуля к сумме может изменить результат:

>>> getcontext().prec = 3
>>> Decimal('3.4445') + Decimal('1.0023')
Decimal('4.45')
>>> Decimal('3.4445') + Decimal(0) + Decimal('1.0023')
Decimal('4.44')

Этот метод реализует в to-number операцию спецификации IBM. Если аргумент - строка, никакой лидирующий или завершающего пробела или подчеркивания не разрешены.

create_decimal_from_float(f)

Создание новой Decimal сущности из плавающего f, но округление с использованием self в качестве контекста. В отличие от метода класса Decimal.from_float(), к преобразованию применяется точность контекста, метод округления, флаги и ловушки.

>>> context = Context(prec=5, rounding=ROUND_DOWN)
>>> context.create_decimal_from_float(math.pi)
Decimal('3.1415')
>>> context = Context(prec=5, traps=[Inexact])
>>> context.create_decimal_from_float(math.pi)
Traceback (most recent call last):
    ...
decimal.Inexact: None

Добавлено в версии 3.1.

Etiny()

Возвращает значение, равное Emin - prec + 1, которое является минимальной экспонентой значения для субнормальных результатов. Когда происходит переполнение экспонента устанавливается в Etiny.

Etop()

Возвращает значение, равное Emax - prec + 1.

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

abs(x)

Возвращает абсолютное значение x.

add(x, y)

Возвращает сумму x и y.

canonical(x)

Возвращает тот же десятичный объект x.

compare(x, y)

Сравнивает x и y численно.

compare_signal(x, y)

Сравнивает значения двух операндов численно.

compare_total(x, y)

Сравнивает два операнда, используя их абстрактное представление.

compare_total_mag(x, y)

Сравнивает два операнда, используя их абстрактное представление, игнорируя знак.

copy_abs(x)

Возвращает копию x со знаком, установленным в 0.

copy_negate(x)

Возвращает копию x с инвертированным знаком.

copy_sign(x, y)

Копирует знак с y на x.

divide(x, y)

Возвращает x делёного на y.

divide_int(x, y)

Возвращает x, деленого на y, усеченная до целого числа.

divmod(x, y)

Делит два числа и возвращает целую часть результата.

exp(x)

Степень „e ** x“.

fma(x, y, z)

Возвращает x, умноженное на y, плюс z.

is_canonical(x)

Возвращает True, если x является каноническим; в противном случае возвращает False.

is_finite(x)

Возвращает True, если x является конечным; в противном случае возвращает False.

is_infinite(x)

Возвращает True, если x бесконечно; в противном случае возвращает False.

is_nan(x)

Возвращает значение True, если x является qNaN или sNaN; в противном случае возвращает False.

is_normal(x)

Возвращает True, если x является обычным числом; в противном случае возвращает False.

is_qnan(x)

Возвращает True, если x является тихим NaN; в противном случае возвращает False.

is_signed(x)

Возвращает True, если x является отрицательным; в противном случае возвращает False.

is_snan(x)

Возвращает значение True, если x является сигнальным NaN; в противном случае возвращает False.

is_subnormal(x)

Возвращает True, если x является субнормальной; в противном случае возвращает False.

is_zero(x)

Возвращает True, если x равен нулю; в противном случае возвращает False.

ln(x)

Возвращает натуральный (по основанию e) логарифм x.

log10(x)

Возвращает логарифм x по основанию 10.

logb(x)

Возвращает степень величины MSD операнда.

logical_and(x, y)

Применяет логическую операцию and между цифрами каждого операнда.

logical_invert(x)

Инвертирует все цифры в x.

logical_or(x, y)

Применяет логическую операцию or между цифрами каждого операнда.

logical_xor(x, y)

Применяет логическую операцию xor между цифрами каждого операнда.

max(x, y)

Сравнивает два значения численно и возвращает максимум.

max_mag(x, y)

Сравнивает значения численно с их игнорируемым знаком.

min(x, y)

Сравнивает два значения численно и возвращает минимум.

min_mag(x, y)

Сравнивает значения численно с их игнорируемым знаком.

minus(x)

Минус соответствует унарному префиксу минус оператор в Python.

multiply(x, y)

Возвращает умножение x и y.

next_minus(x)

Возвращает наибольшее представимое число, меньшее чем x.

next_plus(x)

Возвращает наименьшее представимое число, превышающее x.

next_toward(x, y)

Возвращает число, наиболее близкое к x, в направлении к y.

normalize(x)

Сводит x к простейшей форме.

number_class(x)

Возвращает указание класса x.

plus(x)

Плюс соответствует унарному префиксу плюс оператор в Python. Эта операция применяет точность контекста и округления, поэтому она не является операцией идентификации.

power(x, y, modulo=None)

Возвращает x в степени y, уменьшенное по модулю modulo, если передано.

С двумя аргументами вычислит x**y. Если x отрицательный, то y должен быть интегральным. Результат будет неточным, если y не является интегральным и результат является конечным и может быть выражен точно в «точности» цифр. Используется режим округления из контекста. Результаты всегда правильно округляются в Python версии.

Изменено в версии 3.3: Модуль C вычисляет power() с точки зрения правильно округленных функций exp() и ln(). Результат четко определен, но только «почти всегда правильно округлен».

С тремя аргументами вычислить (x**y) % modulo. Для формы с тремя аргументами хранятся следующие ограничения для аргументов:

  • все три аргумента должны быть интегральными,
  • y должен быть неотрицательным,
  • по крайней мере один из x или y должен быть отличным от нуля
  • modulo, должно быть отличным от нуля и содержать больше цифр „точности“

Значение, следующее из Context.power(x, y, modulo), равено значению, которое было бы получено, вычислив (x**y) % modulo с неограниченной точностью, но вычислен более эффективно. Экспонента результата равна нулю, независимо от степеней x, y и modulo. Результат всегда точный.

quantize(x, y)

Возвращает значение, равный x (округленный), имеющий степень y.

radix()

Возвращает 10, так как это Decimal,:)

remainder(x, y)

Возвращает остаток от целочисленного деления.

Знак результата, если он ненулевой, совпадает с знаком исходного делимого.

remainder_near(x, y)

Возвращает x - y * n, где n - целое число, ближайшее к точному значению x / y (если результат 0, то его знаком будет знак x).

rotate(x, y)

Возвращает повернутую копию x, y раз.

same_quantum(x, y)

Возвращает значение True, если два операнда имеют одинаковую степень.

scaleb(x, y)

Возвращает первый операнд после добавления второго значение его exp.

shift(x, y)

Возвращает сдвинутую копию x, y раз.

sqrt(x)

Квадратный корень неотрицательного числа для точности контекста.

subtract(x, y)

Возвращает разницу между x и y.

to_eng_string(x)

Преобразовать в строку, используя инженерную нотацию, если требуется степень.

У инженерной нотации должна быть степень, кратную 3. Это может оставить до 3 цифр слева от десятичного знака и может потребовать добавления одного или двух конечных нулей.

to_integral_exact(x)

Округление до целого числа.

to_sci_string(x)

Преобразует число в строку, используя научную нотацию.

Константы

Константы в этом разделе относятся только к модулю C. Они также включены в чистую Python версию для совместимости.

  32-bit 64-bit
decimal.MAX_PREC
425000000 999999999999999999
decimal.MAX_EMAX
425000000 999999999999999999
decimal.MIN_EMIN
-425000000 -999999999999999999
decimal.MIN_ETINY
-849999999 -1999999999999999997
decimal.HAVE_THREADS

Значение True. Устарел, потому что Python теперь всегда реализует потоки.

Не рекомендуется, начиная с версии 3.9.

decimal.HAVE_CONTEXTVAR

Значение по умолчанию является True. Если Python скомпилирован --without-decimal-contextvar, версия C использует поточнолокальный, а не корутинлокальный контекст, и значение является False. Это несколько быстрее в некоторых вложенных сценариях контекста.

Добавлено в версии 3.9: backported to 3.7 and 3.8

Режимы округления

decimal.ROUND_CEILING

Округление к Infinity.

decimal.ROUND_DOWN

Округление по направлению к нулю.

decimal.ROUND_FLOOR

Округление к -Infinity.

decimal.ROUND_HALF_DOWN

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

decimal.ROUND_HALF_EVEN

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

decimal.ROUND_HALF_UP

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

decimal.ROUND_UP

Округление от нуля.

decimal.ROUND_05UP

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

Сигналы

Сигналы представляют собой условия, возникающие во время вычислений. Каждому соответствует один флаг контекста и один активатор ловушки контекста.

Флаг контекста устанавливается при каждом обнаружении условия. После вычисления флаги могут проверяться в информационных целях (для сущность, чтобы определить, было ли вычисление точным). После проверки флагов обязательно очистите все флаги перед началом следующего вычисления.

Если активатор ловушки контекста установлен для сигнала, то это условие вызывает исключение Python. Например, если задана ловушка DivisionByZero, то при обнаружении условия возникает исключение DivisionByZero.

class decimal.Clamped

Изменение степени для соответствия ограничениям представления.

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

class decimal.DecimalException

Базовый класс для других сигналов и подкласс ArithmeticError.

class decimal.DivisionByZero

Сигнализация деления не бесконечного числа на ноль.

Может происходить с делением, делением по модулю или при увеличении числа до отрицательной степени. Если этот сигнал не захвачен, возвращает Infinity или -Infinity со знаком, определенным входами в расчет.

class decimal.Inexact

Указывает, что произошло округление, и результат не является точным.

Сигналы при отбрасывании ненулевых цифр при округлении. Возвращается округленный результат. Использование сигнального флага или ловушки, чтобы обнаружить, когда результаты неточны.

class decimal.InvalidOperation

Выполнена недопустимая операция.

Указывает, что запрошена операция, которая не имеет смысла. Если не попал в ловушку, возвращает значение NaN. Возможные причины включают в себя:

Infinity - Infinity
0 * Infinity
Infinity / Infinity
x % 0
Infinity % x
sqrt(-x) and x > 0
0 ** 0
x ** (non-integer)
x ** Infinity
class decimal.Overflow

Числовое переполнение.

Указывает, что степень больше чем Emax после округления. Если результат не захвачен, то он зависит от режима округления, либо от перемещения внутрь до наибольшего представляемого конечного числа, либо от округления наружу до Infinity. В любом случае Inexact и Rounded также сигнализируются.

class decimal.Rounded

Округление произошло, хотя информация, возможно, не была потеряна.

Сигнализируется всякий раз, когда округление отбрасывает цифры; даже если эти цифры равны нулю (например, округление 5.00 до 5.0). Если не захвачен, возвращает результат без изменений. Этот сигнал используется, чтобы обнаружить потерю значимых цифр.

class decimal.Subnormal

Степень была ниже, чем Emin до округления.

Возникает, когда результат операции является субнормальным (степень слишком мала). Если не захвачен, возвращает результат без изменений.

class decimal.Underflow

Численный нижний поток с результатом, округленным до нуля.

Возникает при смещении субнормального результата к нулю путем округления. Inexact и Subnormal также сигнализируются.

class decimal.FloatOperation

Обеспечение более строгой семантики при смешивании плавающих и десятичных разрядов.

Если сигнал не захвачен (по умолчанию), смешение плавающих и десятичных разрядов допускается в конструкторе Decimal, create_decimal() и во всех операторах сравнения. И преобразование, и сравнение точны. Любое возникновение смешанной операции фиксируется без необходимости путем установки FloatOperation в флагах контекста. Явные преобразования с помощью from_float() или create_decimal_from_float() не устанавливают флаг.

В противном случае (сигнал захвачен) молчаливы только сравнения равенства и явные преобразования. Все остальные смешанные операции поднимают FloatOperation.

В следующей таблице представлена иерархия сигналов:

exceptions.ArithmeticError(exceptions.Exception)
    DecimalException
        Clamped
        DivisionByZero(DecimalException, exceptions.ZeroDivisionError)
        Inexact
            Overflow(Inexact, Rounded)
            Underflow(Inexact, Rounded, Subnormal)
        InvalidOperation
        Rounded
        Subnormal
        FloatOperation(DecimalException, exceptions.TypeError)

Заметки о плавающей точке

Уменьшение погрешности округления с повышенной точностью

Использование десятичной плавающей точки устраняет ошибку десятичного представления (позволяя представить 0.1 точно); однако некоторые операции могут по-прежнему вызывать ошибку округления, когда ненулевые цифры превышают фиксированную точность.

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

# Примеры из Получисловые алгоритмы, раздел 4.2.2.
>>> from decimal import Decimal, getcontext
>>> getcontext().prec = 8

>>> u, v, w = Decimal(11111113), Decimal(-11111111), Decimal('7.51111111')
>>> (u + v) + w
Decimal('9.5111111')
>>> u + (v + w)
Decimal('10')

>>> u, v, w = Decimal(20000), Decimal(-6), Decimal('6.0000003')
>>> (u*v) + (u*w)
Decimal('0.01')
>>> u * (v+w)
Decimal('0.0060000')

Модуль decimal позволяет восстановить идентичность, достаточно расширяя точность, чтобы избежать потери значимости:

>>> getcontext().prec = 20
>>> u, v, w = Decimal(11111113), Decimal(-11111111), Decimal('7.51111111')
>>> (u + v) + w
Decimal('9.51111111')
>>> u + (v + w)
Decimal('9.51111111')
>>>
>>> u, v, w = Decimal(20000), Decimal(-6), Decimal('6.0000003')
>>> (u*v) + (u*w)
Decimal('0.0060000')
>>> u * (v+w)
Decimal('0.0060000')

Специальные значения

Система чисел для модуля decimal предоставляет специальные значения включая NaN, sNaN, -Infinity, Infinity, и два ноля, +0 и -0.

Бесконечности можно построить непосредственно с помощью: Decimal('Infinity'). Кроме того, они могут возникать при делении на ноль, когда сигнал DivisionByZero не захватывался. Аналогично, когда сигнал Overflow не захвачен, бесконечность может быть результатом округления за пределы наибольшего представляемого числа.

Бесконечности являются знаковыми (аффинными) и могут быть используемы в арифметических операциях, где они рассматриваются как очень большие, неопределённые числа. Для сущность добавление константы к бесконечности даёт ещё один бесконечный результат.

Некоторые операции являются неопределенными и возвращает NaN или, если сигнал InvalidOperation захвачен, вызывают исключение. Например, 0/0 возвращает NaN, что означает «не число». Это разнообразие NaN является тихим и, после создания, будет проходить через другие вычисления всегда приводит к другой NaN. Это поведение может быть полезно для ряда вычислений, у которых иногда есть недостающие исходные данные — это позволяет расчетам продолжаться при пометке конкретных результатов как недопустимых.

Вариант - sNaN, который сигнализирует вместо того, чтобы остаться тихим после каждой операции. Это полезное возвращаемое значение, когда недопустимый результат должен прервать расчет для специальной обработки.

Поведение операторов сравнения Python’а может быть немного странным, когда речь идет о NaN. Тест на равенство, где один из операндов - тихий или сигнальный NaN всегда, возвращает False (делая Decimal('NaN')==Decimal('NaN')), в то время как тест на неравенство всегда возвращает True. Попытка сравнения двух десятичных разрядов с использованием любого из операторов <, <=, > или >= поднимет сигнал InvalidOperation, если операнд является NaN, и возвращает False, если этот сигнал не захвачен. Обратите внимание, что общая десятичная арифметическая спецификация не определяет поведение прямых сравнений; эти правила для сравнения с участием NaN были взяты из стандарта IEEE 854 (см. таблицу 3 в разделе 5.7). Чтобы обеспечить строгое соблюдение стандартов, используйте методы compare() и compare-signal().

Нулевые значения со знаком могут быть получены в результате вычислений, которые не были выполнены. Они сохраняют знак, который был бы получен, если бы расчет был выполнен с большей точностью. Так как их величина равна нулю, как положительные, так и отрицательные нули рассматриваются как равные и их знак является информационным.

В дополнение к двум знаковым нулям, которые различны, но равны, есть различные представления нуля с различной точностью, но эквивалентны в значении. К этому нужно привыкнуть. Для глаза, привыкшего к нормализованные представления с плавающей точкой, не сразу очевидно, что следующий расчет возвращает значение, равное нулю:

>>> 1 / Decimal('Infinity')
Decimal('0E-1000026')

Работа с потоки

Функция getcontext() получает доступ к другому объекту Context для каждого потока. Наличие отдельных контекстов потока означает, что потоки могут вносить изменения (например, getcontext().prec=10) без вмешательства в другие потоки.

Аналогично, функция setcontext() автоматически назначает свою цель текущему потоку.

Если setcontext() не был вызван до getcontext(), то getcontext() автоматически создаст новый контекст для использования в текущем потоке.

Новый контекст копируется из прототипа контекста под названием DefaultContext. Чтобы управлять дефолтами так, чтобы каждый поток использовал те же значения всюду в приложении, непосредственно измените объект DefaultContext. Это должно быть сделано перед началом любого потока так, чтобы не было условия гонки между потоками, вызвав getcontext(). Например:

# Установить значения по умолчанию для всех потоков, которые будут запущены
DefaultContext.prec = 12
DefaultContext.rounding = ROUND_DOWN
DefaultContext.traps = ExtendedContext.traps.copy()
DefaultContext.traps[InvalidOperation] = 1
setcontext(DefaultContext)

# После этого можно запускать потоки
t1.start()
t2.start()
t3.start()
 . . .

Рецепты

Вот несколько рецептов, которые служат в качестве полезных функций и которые демонстрируют способы работы с классом Decimal:

def moneyfmt(value, places=2, curr='', sep=',', dp='.',
             pos='', neg='-', trailneg=''):
    """Преобразовать Decimal в денежную форматированную строку.

    places:  необходимое количество знаков после запятой
    curr:    необязательный символ валюты перед знаком (может быть пустым)
    sep:     дополнительный разделитель группировки (запятая, точка, пробел или пусто)
    dp:      индикатор десятичной точки (запятая или точка)
             указывать только как пустые, когда места нулевые
    pos:     необязательный знак для положительных чисел: «+», пробел или пусто
    neg:     необязательный знак для отрицательных чисел: '-', '(', пробел или пусто
    trailneg:дополнительный индикатор трейлинг минус:' -',')', пробел или пусто

    >>> d = Decimal('-1234567.8901')
    >>> moneyfmt(d, curr='$')
    '-$1,234,567.89'
    >>> moneyfmt(d, places=0, sep='.', dp='', neg='', trailneg='-')
    '1.234.568-'
    >>> moneyfmt(d, curr='$', neg='(', trailneg=')')
    '($1,234,567.89)'
    >>> moneyfmt(Decimal(123456789), sep=' ')
    '123 456 789.00'
    >>> moneyfmt(Decimal('-0.02'), neg='<', trailneg='>')
    '<0.02>'

    """
    q = Decimal(10) ** -places      # 2 places --> '0.01'
    sign, digits, exp = value.quantize(q).as_tuple()
    result = []
    digits = list(map(str, digits))
    build, next = result.append, digits.pop
    if sign:
        build(trailneg)
    for i in range(places):
        build(next() if digits else '0')
    if places:
        build(dp)
    if not digits:
        build('0')
    i = 0
    while digits:
        build(next())
        i += 1
        if i == 3 and digits:
            i = 0
            build(sep)
    build(curr)
    build(neg if sign else pos)
    return ''.join(reversed(result))

def pi():
    """Вычислить Пи с текущей точностью.

    >>> print(pi())
    3.141592653589793238462643383

    """
    getcontext().prec += 2  # дополнительные цифры для промежуточных шагов
    three = Decimal(3)      # заменить "three=3.0" для регулярных флоутов
    lasts, t, s, n, na, d, da = 0, three, 3, 1, 0, 0, 24
    while s != lasts:
        lasts = s
        n, na = n+na, na+8
        d, da = d+da, da+32
        t = (t * n) / d
        s += t
    getcontext().prec -= 2
    return +s               # унарный плюс применяется новая точность

def exp(x):
    """Возвращает e, возведенное в степень x. Тип результата соответствует типу ввода.

    >>> print(exp(Decimal(1)))
    2.718281828459045235360287471
    >>> print(exp(Decimal(2)))
    7.389056098930650227230427461
    >>> print(exp(2.0))
    7.38905609893
    >>> print(exp(2+0j))
    (7.38905609893+0j)

    """
    getcontext().prec += 2
    i, lasts, s, fact, num = 0, 0, 1, 1, 1
    while s != lasts:
        lasts = s
        i += 1
        fact *= i
        num *= x
        s += num / fact
    getcontext().prec -= 2
    return +s

def cos(x):
    """Возвращает косинус x, измеренный в радианах.

    Аппроксимация рядов Тейлора лучше всего работает при малом значении X.
    Для больших значений сначала вычислите x = x % (2 * pi).

    >>> print(cos(Decimal('0.5')))
    0.8775825618903727161162815826
    >>> print(cos(0.5))
    0.87758256189
    >>> print(cos(0.5+0j))
    (0.87758256189+0j)

    """
    getcontext().prec += 2
    i, lasts, s, fact, num, sign = 0, 0, 1, 1, 1, 1
    while s != lasts:
        lasts = s
        i += 2
        fact *= i * (i-1)
        num *= x * x
        sign *= -1
        s += num / fact * sign
    getcontext().prec -= 2
    return +s

def sin(x):
    """Возвращает синус x, измеренный в радианах.

    Аппроксимация рядов Тейлора лучше всего работает при малом значении X.
    Для больших значений сначала вычислите x = x % (2 * pi).

    >>> print(sin(Decimal('0.5')))
    0.4794255386042030002732879352
    >>> print(sin(0.5))
    0.479425538604
    >>> print(sin(0.5+0j))
    (0.479425538604+0j)

    """
    getcontext().prec += 2
    i, lasts, s, fact, num, sign = 1, 0, x, 1, x, 1
    while s != lasts:
        lasts = s
        i += 2
        fact *= i * (i-1)
        num *= x * x
        sign *= -1
        s += num / fact * sign
    getcontext().prec -= 2
    return +s

Часто задаваемые вопросы по Decimal

Вопрос. Громоздко набирать decimal.Decimal('1234.5'). Есть ли способ минимизировать ввод при использовании интерактивного интерпретатора?

Ответ. Некоторые пользователи сокращают конструктор только одной буквой:

>>> D = decimal.Decimal
>>> D('1.23') + D('3.45')
Decimal('4.68')

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

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

>>> TWOPLACES = Decimal(10) ** -2       # некоторый Decimal('0.01')
>>> # Округлить до двух знаков
>>> Decimal('3.214').quantize(TWOPLACES)
Decimal('3.21')
>>> # Убедитесь, что число не превышает двух мест
>>> 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

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

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

>>> a = Decimal('102.72')           # Начальные значения с фиксированной точкой
>>> b = Decimal('3.17')
>>> a + b                           # Дополнение сохраняет фиксированную точку
Decimal('105.89')
>>> a - b
Decimal('99.55')
>>> a * 42                          # Так же как и целочисленное умножение
Decimal('4314.24')
>>> (a * b).quantize(TWOPLACES)     # Должен квантовать нецелое умножение
Decimal('325.62')
>>> (b / a).quantize(TWOPLACES)     # И квантовать деление
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)                       # Автоматически сохранять фиксированную точку
Decimal('325.62')
>>> div(b, a)
Decimal('0.03')

Вопрос. Есть много способов выразить одно и то же значение. Номера 200, 200.000, 2E2 и 02E+4 у всех есть тот же значение в различной точности. Есть ли способ преобразовать их к единому узнаваемому каноническому значению?

Ответ. Метод normalize() сопоставляет все эквивалентные значения одному представителю:

>>> values = map(Decimal, '200 200.000 2E2 .02E+4'.split())
>>> [v.normalize() for v in values]
[Decimal('2E+2'), Decimal('2E+2'), Decimal('2E+2'), Decimal('2E+2')]

Вопрос. Некоторые десятичные значения всегда печатаются с экспоненциальной нотацией. Есть ли способ получить неэкспоненциальное представление?

А. Для некоторых значения экспоненциальная нотация является единственным способом выражения числа значимых мест в коэффициенте. Например, выражение 5.0E+3 as 5000 сохраняет значение постоянной, но не может показать значение оригинала в двух местах.

Если приложение не заботится об отслеживании значимости, легко удалить экспонентные и конечные нули, потеряв значимость, но сохранив значение неизменным:

>>> def remove_exponent(d):
...     return d.quantize(Decimal(1)) if d == d.to_integral() else d.normalize()
>>> remove_exponent(Decimal('5E+3'))
Decimal('5000')

Вопрос. Есть ли способ преобразовать обычный float в Decimal?

Ответ. Да, любое двоичное число с плавающей точкой может быть точно выражено как десятичное, хотя точное преобразование может занять больше точности, чем подсказывает интуиция:

>>> Decimal(math.pi)
Decimal('3.141592653589793115997963468544185161590576171875')

Вопрос. В рамках сложных расчетов, как я могу убедиться, что я не получил ложный результат из-за недостаточной точности или аномалий округления.

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

Вопрос. Я заметил, что точность контекста применяется к результатам операций, но не на входы. Есть ли что-то, на что следует обратить внимание при смешивании значений разных точностей?

Ответ. Да. Принцип заключается в том, что все значения считаются точными и поэтому арифметика этих значений. Только результаты округляются. Преимущество для входных данных - это то, что вы печатаете, то и получаете. Недостатком является то, что результаты могут выглядеть странно, если вы забудете, что входные данные не были округлены:

>>> getcontext().prec = 3
>>> Decimal('3.104') + Decimal('2.104')
Decimal('5.21')
>>> Decimal('3.104') + Decimal('0.000') + Decimal('2.104')
Decimal('5.20')

Решением является либо повышение точности, либо принудительное округление входных данных с помощью операции унарного плюса:

>>> getcontext().prec = 3
>>> +Decimal('1.23456789')      # унарный плюс запускает округление
Decimal('1.23')

В качестве альтернативы, входные данные можно округлять при создании с помощью метода Context.create_decimal():

>>> Context(prec=5, rounding=ROUND_DOWN).create_decimal('1.2345678')
Decimal('1.2345')

Вопрос. Быстрая ли реализация CPython для больших чисел?

Ответ. Да. В реализациях CPython и PyPy3 версии C/CFFI Decimal модуля интегрируют высокоскоростную библиотеку libmpdec для произвольной точности правильного округленной десятичной арифметики с плавающей точкой [1]. libmpdec использует Карацуба умножение для средних чисел и Теоретическое преобразование числа для очень больших чисел.

Контекст должен быть адаптирован к точной произвольной арифметике точности. Emin и Emax должны всегда устанавливаться в максимальное значение, clamp должен всегда быть 0 (по умолчанию). Установка prec требует определенной осторожности.

Самый простой подход к арифметике больших чисел заключается в использовании максимального значения для prec, а также [2]:

>>> setcontext(Context(prec=MAX_PREC, Emax=MAX_EMAX, Emin=MIN_EMIN))
>>> x = Decimal(2) ** 256
>>> x / 128
Decimal('904625697166532776746648320380374280103671755200316906558262375061821325312')

Для неточных результатов на 64-разрядных платформах слишком велико значение MAX_PREC, а доступной памяти будет недостаточно:

>>> Decimal(1) / 3
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
MemoryError

На системах со сверхраспределением (например, Linux), более сложный подход должен приспособить prec на сумму доступного ОЗУ. Предположим, что у вас есть 8GB ОЗУ, и ожидание 10 одновременных операндов, используя максимум 500MB каждый:

>>> import sys
>>>
>>> # Максимальное количество цифр для одного операнда, использующего 500 МБ в 8-байтовых словах
>>> # с 19 цифрами на слово (4 байта и 9 цифр для 32-битной сборки):
>>> maxdigits = 19 * ((500 * 1024**2) // 8)
>>>
>>> # Проверьте, что это работает:
>>> c = Context(prec=maxdigits, Emax=MAX_EMAX, Emin=MIN_EMIN)
>>> c.traps[Inexact] = True
>>> setcontext(c)
>>>
>>> # Заполнить доступную точность с помощью девяток:
>>> x = Decimal(0).logical_invert() * 9
>>> sys.getsizeof(x)
524288112
>>> x + 2
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  decimal.Inexact: [<class 'decimal.Inexact'>]

В целом (и особенно в системах без чрезмерного распределения) рекомендуется оценивать еще более жесткие границы и устанавливать ловушку Inexact, если ожидается, что все расчеты будут точными.

[1]

Добавлено в версии 3.3.

[2]

Изменено в версии 3.9: Этот подход теперь работает для всех точных результатов, кроме нецелочисленных степеней. Также до 3.7 и 3.8.