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

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


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

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

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

  • В отличие от аппаратных двоичных чисел с плавающей запятой, у модуля decimal настраиваемая пользователем точность (по умолчанию 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 предоставляет все необходимые части стандарта. При необходимости программист полностью контролирует округление и обработку сигналов. Он включает в себя возможность принудительного выполнения точной арифметики с помощью исключений для блокировки любых неточных операций.

  • Модуль 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.

Сигналы — это группы исключительных состояний, возникающих в процессе вычислений. В зависимости от потребностей приложения сигналы могут игнорироваться, рассматриваться как информационные или рассматриваться как исключения. Сигналы в десятичном модуле: 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.

Десятичные объекты

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, возникает исключение; в противном случае конструктор возвращает новый Decimal со значением NaN.

После создания объекты Decimal неизменны.

Изменено в версии 3.2: Аргументом конструктора теперь может быть экземпляр float.

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

Изменено в версии 3.6: Подчеркивания разрешены для группировки, как и для целочисленных литералов и литералов с плавающей запятой в коде.

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

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

>>> (-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 и другими числовыми типами.

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

adjusted()

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

as_integer_ratio()

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

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

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

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

as_tuple()

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

canonical()

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

compare(other, context=None)

Сравнить значения двух экземпляров Decimal. compare() возвращает экземпляр Decimal, и если один из операндов — 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(), за исключением того, что все сигналы NaN. То есть, если ни один из операндов не является сигнальным 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 может вызвать InvalidOperation, если второй операнд не может быть точно преобразован.

compare_total_mag(other, context=None)

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

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

conjugate()

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

copy_abs()

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

copy_negate()

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

copy_sign(other, context=None)

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

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

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

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, если аргумент является конечным числом normal. Возвращает 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)

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

log10(context=None)

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

logb(context=None)

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

logical_and(other, context=None)

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

logical_invert(context=None)

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

logical_or(other, context=None)

logical_or() — это логическая операция, которая принимает два логических операнда (см. Логические операнды). Результатом будет 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)

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

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. Это гарантирует, что, если нет состояния ошибки, квантованная экспонента всегда равна показателю правого операнда.

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

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

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

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

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() ожидают, что их аргументы будут логическими операндами. Логический операнд — это экземпляр 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=28, rounding=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')

Этот метод реализует операцию к номеру спецификации 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)

Возвращает тот же объект Decimal 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)

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

log10(x)

Возвращает десятичный логарифм x.

logb(x)

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

logical_and(x, y)

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

logical_invert(x)

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

logical_or(x, y)

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

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 результаты всегда округляются правильно.

Decimal(0) ** Decimal(0) приводит к InvalidOperation, а если InvalidOperation не захватывается, то отдаёт Decimal('NaN').

Изменено в версии 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: перенесён на 3.7 и 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 места --> '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 как 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 для произвольной точности правильного округленной десятичной арифметики с плавающей точкой. libmpdec использует умножение Карацуба для средних чисел и Теоретическое преобразование числа для очень больших чисел.

>>> c = getcontext()
>>> c.prec = MAX_PREC
>>> c.Emax = MAX_EMAX
>>> c.Emin = MIN_EMIN

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