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 предпочтительнее в приложениях бухгалтерского учета, которые имеют строгие инварианты равенства.
  • Модуль 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. Применяются все обычные математические операции и специальные методы. Аналогично, объекты Decimal можно копировать, сериализовать, печатать, использовать в качестве ключей словарей, элементов множеств, сравнивать, сортировать и преобразовывать в другой тип (например, 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. Однако сравнить экземпляр Decimal x с другим числом y с помощью операторов сравнения Python возможно. Это позволяет избежать путаницы при сравнении чисел разных типов.

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

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

adjusted()

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

as_integer_ratio()

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

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

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

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

as_tuple()

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

canonical()

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

compare(other, context=None)

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

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

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

compare_total(other, context=None)

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

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

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

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

compare_total_mag(other, context=None)

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

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

conjugate()

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

copy_abs()

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

copy_negate()

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

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

Слияние умножения и сложения. Возвращает 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)

Возвращает логарифм по основанию 10 операнда. Результат корректно округляется с использованием режима округления 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. Это гарантирует, что, за исключением условия ошибки, показатель степени quantized всегда равен показателю степени правого операнда.

Также в отличие от других операций, 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. Если задано, применяет округление; в противном случае используется метод округления в предоставленном контексте или в текущем контексте.

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

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

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

Оператор унарного минуса.

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)

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

power(x, y, modulo=None)

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

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

Decimal(0) ** Decimal(0) приводит к InvalidOperation, а если InvalidOperation не приводит к ошибке, то приводит к Decimal('NaN').

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

С тремя аргументами вычисляет xy mod z.

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

Значение, полученное из 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 (если результат 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.9: переработано для 3.7 и 3.8.

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

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

В. Вводить 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')]

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

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

>>> 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 C/CFFI версии модуля decimal интегрируют высокоскоростную библиотеку libmpdec для арифметики десятичных чисел с произвольной точностью и правильным округлением 1. libmpdec использует умножение Карацубы для чисел среднего размера и преобразование по теореме о числе для очень больших чисел.

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

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

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

Для неточных результатов 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–2023 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.11/library/decimal.html

Spec-Zone.ru

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