Spec-Zone.ru › Python 3.11

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.
  • В отличие от аппаратной двоичной арифметики с плавающей точкой, модуль 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.

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

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

См. также

  • Спецификация общей десятичной арифметики IBM: Спецификация общей десятичной арифметики.

Краткое руководство

Обычно работу с десятичными числами начинают с импорта модуля, просмотра текущего контекста с помощью 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       # Set a new precision

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

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

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

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

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

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

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

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

Объекты десятичного числа с плавающей точкой обладают многими свойствами других встроенных числовых типов, таких как float и int. Применимы все обычные математические операции и специальные методы. Аналогично, объекты Decimal можно копировать, сериализовать с помощью pickle, выводить на печать, использовать в качестве ключей словарей и элементов множеств, сравнивать, сортировать и преобразовывать в другой тип (например, 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, а для NaN — ValueError.

Добавлено в версии 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) e**x для заданного числа. Результат правильно округляется с использованием режима округления ROUND_HALF_EVEN.

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

Альтернативный конструктор, принимающий только экземпляры float или int.

Обратите внимание: 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)

Возвращает натуральный логарифм (по основанию 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)

Используется для получения канонических значений класса эквивалентности в текущем или указанном контексте.

Семантика этого метода совпадает с унарным оператором «плюс», за исключением того, что конечный результат приводится к простейшей форме: все конечные нули удаляются, а знак сохраняется. То есть, пока коэффициент ненулевой и кратен десяти, коэффициент делится на десять, а показатель степени увеличивается на 1. В противном случае (если коэффициент равен нулю) показатель степени устанавливается в 0. Во всех случаях знак остаётся неизменным.

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

Обратите внимание, что округление выполняется до приведения к простейшей форме.

В последних версиях спецификации эта операция также называется reduce.

number_class(context=None)

Возвращает строку с описанием класса операнда. Возвращаемое значение — одна из следующих десяти строк.

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

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

Также в отличие от других операций, quantize никогда не вызывает сигнал 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)

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

same_quantum(other, context=None)

Проверяет, одинаковы ли показатели степени у self и other или являются ли оба значения NaN.

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

scaleb(other, context=None)

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

shift(other, context=None)

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

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, \*\*kwargs)

Возвращает менеджер контекста, который при входе в инструкцию with установит текущий контекст активного потока в копию ctx, а при выходе из инструкции with восстановит предыдущий контекст. Если контекст не указан, используется копия текущего контекста. Аргумент kwargs используется для установки атрибутов нового контекста.

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

from decimal import localcontext

with localcontext() as ctx:
    ctx.prec = 42   # Perform a high precision calculation
    s = calculate_something()
s = +s  # Round the final result back to the default precision

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

from decimal import localcontext

with localcontext(prec=42) as ctx:
    s = calculate_something()
s = +s

Вызывает исключение TypeError, если kwargs задает атрибут, который Context не поддерживает. Вызывает исключение TypeError или ValueError, если kwargs задает недопустимое значение атрибута.

Изменено в версии 3.11: Функция localcontext() теперь поддерживает установку атрибутов контекста с помощью именованных аргументов.

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

class decimal.BasicContext

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

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

class decimal.ExtendedContext

Это стандартный контекст, определенный спецификацией General Decimal Arithmetic. Точность равна девяти. Округление выполняется по правилу ROUND_HALF_EVEN. Все флаги сброшены. Ни одна ловушка не включена (поэтому во время вычислений исключения не вызываются).

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

class decimal.DefaultContext

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

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

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

Значения по умолчанию: Context.prec=28, Context.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')

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

Возвращает тот же объект 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 (по основанию e).

log10(x)

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

logb(x)

Возвращает показатель степени старшей значащей цифры операнда.

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 не является целым числом, а результат — конечным и точно представимым заданным числом цифр «precision». Используется режим округления контекста. В версии Python результаты всегда округляются правильно.

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

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

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

  • все три аргумента должны быть целыми числами
  • y должно быть неотрицательным
  • как минимум один из аргументов x или y должен быть ненулевым
  • modulo должен быть ненулевым и содержать не более «precision» цифр

Значение, полученное в результате 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)

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

shift(x, y)

Возвращает копию x со сдвигом на y позиций.

sqrt(x)

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

subtract(x, y)

Возвращает разность x и y.

to_eng_string(x)

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

В инженерной нотации показатель степени кратен 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 собран configured using the --without-decimal-contextvar option, версия на C использует контекст, локальный для потока, а не для сопрограммы, и значение равно False. В некоторых сценариях с вложенными контекстами это работает немного быстрее.

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

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

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

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

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

class decimal.Rounded

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

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

class decimal.Subnormal

До округления показатель степени был меньше Emin.

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

class decimal.Underflow

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

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

class decimal.FloatOperation

Включает более строгую семантику при смешивании чисел с плавающей точкой и Decimal.

Если сигнал не перехватывается (по умолчанию), смешивание чисел с плавающей точкой и Decimal допускается в конструкторе 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); однако некоторые операции всё ещё могут приводить к ошибкам округления, когда число ненулевых цифр превышает фиксированную точность.

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

# Examples from Seminumerical Algorithms, Section 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. Попытка сравнить два значения Decimal с помощью любого из операторов <, <=, > или >= подаёт сигнал InvalidOperation, если один из операндов — NaN, и возвращает False, если этот сигнал не перехватывается. Обратите внимание: спецификация General Decimal Arithmetic не определяет поведение прямых сравнений; эти правила сравнений с участием 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(). Например:

# Set applicationwide defaults for all threads about to be launched
DefaultContext.prec = 12
DefaultContext.rounding = ROUND_DOWN
DefaultContext.traps = ExtendedContext.traps.copy()
DefaultContext.traps[InvalidOperation] = 1
setcontext(DefaultContext)

# Afterwards, the threads can be started
t1.start()
t2.start()
t3.start()
 . . .

Примеры

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

def moneyfmt(value, places=2, curr='', sep=',', dp='.',
             pos='', neg='-', trailneg=''):
    """Convert Decimal to a money formatted string.

    places:  required number of places after the decimal point
    curr:    optional currency symbol before the sign (may be blank)
    sep:     optional grouping separator (comma, period, space, or blank)
    dp:      decimal point indicator (comma or period)
             only specify as blank when places is zero
    pos:     optional sign for positive numbers: '+', space or blank
    neg:     optional sign for negative numbers: '-', '(', space or blank
    trailneg:optional trailing minus indicator:  '-', ')', space or blank

    >>> 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():
    """Compute Pi to the current precision.

    >>> print(pi())
    3.141592653589793238462643383

    """
    getcontext().prec += 2  # extra digits for intermediate steps
    three = Decimal(3)      # substitute "three=3.0" for regular floats
    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               # unary plus applies the new precision

def exp(x):
    """Return e raised to the power of x.  Result type matches input type.

    >>> 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):
    """Return the cosine of x as measured in radians.

    The Taylor series approximation works best for a small value of x.
    For larger values, first compute 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):
    """Return the sine of x as measured in radians.

    The Taylor series approximation works best for a small value of x.
    For larger values, first compute 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       # same as Decimal('0.01')
>>> # Round to two places
>>> Decimal('3.214').quantize(TWOPLACES)
Decimal('3.21')
>>> # Validate that a number does not exceed two places
>>> Decimal('3.21').quantize(TWOPLACES, context=Context(traps=[Inexact]))
Decimal('3.21')
>>> Decimal('3.214').quantize(TWOPLACES, context=Context(traps=[Inexact]))
Traceback (most recent call last):
   ...
Inexact: None

В. После получения корректных входных значений с двумя десятичными знаками как сохранить этот инвариант во всём приложении?

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

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

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

>>> def mul(x, y, fp=TWOPLACES):
...     return (x * y).quantize(fp)
>>> def div(x, y, fp=TWOPLACES):
...     return (x / y).quantize(fp)
>>> mul(a, b)                       # Automatically preserve fixed-point
Decimal('325.62')
>>> div(b, a)
Decimal('0.03')

В. Одно и то же значение можно выразить разными способами. Числа 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')]

В. Когда в ходе вычисления происходит округление?

О. Оно происходит после вычисления. Согласно принципу, лежащему в основе спецификации decimal, числа считаются точными и создаются независимо от текущего контекста. Их точность может даже превышать точность текущего контекста. Вычисления выполняются с этими точными входными данными, а затем к результату вычисления применяются округление (или другие операции контекста):

>>> getcontext().prec = 5
>>> pi = Decimal('3.1415926535')   # More than 5 digits
>>> pi                             # All digits are retained
Decimal('3.1415926535')
>>> pi + 0                         # Rounded after an addition
Decimal('3.1416')
>>> pi - Decimal('0.00005')        # Subtract unrounded numbers, then round
Decimal('3.1415')
>>> pi + 0 - Decimal('0.00005').   # Intermediate values are rounded
Decimal('3.1416')

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

О. Для некоторых значений экспоненциальная форма — единственный способ выразить число значащих разрядов коэффициента. Например, запись 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')

В. Можно ли преобразовать обычное число с плавающей точкой в Decimal?

О. Да, любое двоичное число с плавающей точкой можно точно представить в виде 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')      # unary plus triggers rounding
Decimal('1.23')

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

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

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

О. Да. В реализациях CPython и PyPy3 версии модуля decimal на C/CFFI используют высокопроизводительную библиотеку 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')

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

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

В системах с избыточным выделением памяти (например, Linux) более продвинутый подход — установить prec в соответствии с объёмом доступной оперативной памяти. Предположим, у вас есть 8 ГБ оперативной памяти, и вы ожидаете 10 одновременно используемых операндов, каждому из которых требуется не более 500 МБ:

>>> import sys
>>>
>>> # Maximum number of digits for a single operand using 500MB in 8-byte words
>>> # with 19 digits per word (4-byte and 9 digits for the 32-bit build):
>>> maxdigits = 19 * ((500 * 1024**2) // 8)
>>>
>>> # Check that this works:
>>> c = Context(prec=maxdigits, Emax=MAX_EMAX, Emin=MIN_EMIN)
>>> c.traps[Inexact] = True
>>> setcontext(c)
>>>
>>> # Fill the available precision with nines:
>>> 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: Теперь этот подход работает для всех точных результатов, кроме возведения в нецелую степень.

© 2001–2024 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.11/library/decimal.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API