Spec-Zone.ru › Python 3.13

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 для отрицательного), кортеж цифр и целое число экспоненты. Например, 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. Все обычные математические операции и специальные методы применимы. Аналогично, объекты десятичного типа могут быть скопированы, сериализованы, напечатаны, использованы в качестве ключей словарей, элементов множеств, сравниваться, сортироваться и приводиться к другому типу (такому как 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()

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

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

Fused multiply-add. Возвращает 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(), но сравнение выполняется с использованием абсолютных значений операндов.

END_OF_DOCUMENT_MARKER
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 (Not a Number).
  • "sNaN", указывающее, что операнд является сигнализирующим NaN.
quantize(exp, rounding=None, context=None)

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

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

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

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

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

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

radix()

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

remainder_near(other, context=None)

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

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

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

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

same_quantum(other, context=None)

Проверяет, имеют ли self и other одинаковый показатель степени или оба являются 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. Если задан, применяет округление; в противном случае использует метод округления в предоставленном контексте или в текущем контексте.

Десятичные числа могут быть округлены с помощью функции round():

round(number)
round(number, ndigits)

Если ndigits не указано или None, возвращает ближайшее int к number, привязывая к чётному значению при равенстве и игнорируя режим округления контекста Decimal. Вызывает OverflowError, если number — бесконечность, или ValueError, если это NaN (тихий или сигнализирующий).

Если ndigits — int, режим округления контекста учитывается, и возвращается Decimal, представляющий number, округлённый до ближайшего кратного Decimal('1E-ndigits'); в этом случае round(number, ndigits) эквивалентно self.quantize(Decimal('1E-ndigits')). Возвращает Decimal('NaN') если number — тихий NaN. Вызывает InvalidOperation, если number — бесконечность, сигнализирующий NaN, или если длина коэффициента после операции quantize превысит точность текущего контекста. Другими словами, для обычных случаев:

  • если ndigits положительно, возвращает number, округлённый до ndigits десятичных знаков;
  • если ndigits равно нулю, возвращает number, округлённый до ближайшего целого;
  • если ndigits отрицательно, возвращает number, округлённый до ближайшего кратного 10**abs(ndigits).

Например:

>>> from decimal import Decimal, getcontext, ROUND_DOWN
>>> getcontext().rounding = ROUND_DOWN
>>> round(Decimal('3.75'))     # context rounding ignored
4
>>> round(Decimal('3.5'))      # round-ties-to-even
4
>>> round(Decimal('3.75'), 0)  # uses the context rounding
Decimal('3')
>>> round(Decimal('3.75'), 1)
Decimal('3.7')
>>> round(Decimal('3.75'), -1)
Decimal('0E+1')

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

Методы 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)

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

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

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

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

class decimal.ExtendedContext

Это стандартный контекст, определённый в спецификации общей арифметики с десятичными числами. Точность установлена в девять знаков. Округление установлено в 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)
END_OF_DOCUMENT_MARKER

Создаёт новый контекст. Если поле не указано или равно 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)

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

log10(x)

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

logb(x)

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

logical_and(x, y)

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

logical_invert(x)

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

logical_or(x, y)

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

logical_xor(x, y)

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

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. Это может оставить до 3 цифр слева от десятичной точки и может потребовать добавления одной или двух концевых нулей.

to_integral_exact(x)

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

to_sci_string(x)

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

END_OF_DOCUMENT_MARKER

Константы

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

32-разрядный

64-разрядный

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

Округление вверх.

decimal.ROUND_DOWN

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

decimal.ROUND_FLOOR

Округление вниз.

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, 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. Попытка сравнить два десятичных числа с помощью любого из операторов <, <=, > или >= вызовет сигнал 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(). Например:

# 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.13/library/decimal.html

Spec-Zone.ru

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