Spec-Zone.ru › Python 3.8

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, создание Decimal вызывает InvalidOperation:

>>> Decimal("1e9999999999999999999")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
decimal.InvalidOperation: [<class 'decimal.InvalidOperation'>]

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

Десятичные числа хорошо взаимодействуют со многими другими частями Python. Вот небольшой пример:

>>> data = list(map(Decimal, '1.34 1.87 3.45 2.35 1.00 0.03 9.25'.split()))
>>> max(data)
Decimal('9.25')
>>> min(data)
Decimal('0.03')
>>> sorted(data)
[Decimal('0.03'), Decimal('1.00'), Decimal('1.34'), Decimal('1.87'),
 Decimal('2.35'), Decimal('3.45'), Decimal('9.25')]
>>> sum(data)
Decimal('19.29')
>>> a,b,c = data[:3]
>>> str(a)
'1.34'
>>> float(a)
1.34
>>> round(a, 1)
Decimal('1.3')
>>> int(a)
1
>>> a * 5
Decimal('6.70')
>>> a * b
Decimal('2.5058')
>>> c % a
Decimal('0.77')

И некоторые математические функции также доступны для Decimal:

>>> getcontext().prec = 28
>>> Decimal(2).sqrt()
Decimal('1.414213562373095048801688724')
>>> Decimal(1).exp()
Decimal('2.718281828459045235360287471')
>>> Decimal('10').ln()
Decimal('2.302585092994045684017991455')
>>> Decimal('10').log10()
Decimal('1')

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

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

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

Для более сложной работы может быть полезно создание альтернативных контекстов с использованием конструктора Context(). Чтобы сделать альтернативный контекст активным, используйте функцию setcontext().

В соответствии со стандартом, модуль decimal предоставляет два готовых стандартных контекста: BasicContext и ExtendedContext. Первый особенно полезен для отладки, поскольку многие ловушки включены:

>>> myothercontext = Context(prec=60, rounding=ROUND_HALF_DOWN)
>>> setcontext(myothercontext)
>>> Decimal(1) / Decimal(7)
Decimal('0.142857142857142857142857142857142857142857142857142857142857')

>>> ExtendedContext
Context(prec=9, rounding=ROUND_HALF_EVEN, Emin=-999999, Emax=999999,
        capitals=1, clamp=0, flags=[], traps=[])
>>> setcontext(ExtendedContext)
>>> Decimal(1) / Decimal(7)
Decimal('0.142857143')
>>> Decimal(42) / Decimal(0)
Decimal('Infinity')

>>> setcontext(BasicContext)
>>> Decimal(42) / Decimal(0)
Traceback (most recent call last):
  File "<pyshell#143>", line 1, in -toplevel-
    Decimal(42) / Decimal(0)
DivisionByZero: x / 0

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

>>> setcontext(ExtendedContext)
>>> getcontext().clear_flags()
>>> Decimal(355) / Decimal(113)
Decimal('3.14159292')
>>> getcontext()
Context(prec=9, rounding=ROUND_HALF_EVEN, Emin=-999999, Emax=999999,
        capitals=1, clamp=0, flags=[Inexact, Rounded], traps=[])

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

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

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

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

Объекты Decimal

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

Создаёт новый объект Decimal на основе значения value.

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

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

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

Если value является tuple, он должен иметь три компонента: знак (0 для положительного или 1 для отрицательного), кортеж цифр и целое значение экспоненты. Например, 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. Однако можно использовать операторы сравнения Python для сравнения экземпляра Decimal x с другим числом y. Это позволяет избежать путаницы при выполнении сравнений на равенство между числами разных типов.

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

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

adjusted()

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

as_integer_ratio()

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

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

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

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

as_tuple()

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

canonical()

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

compare(other, context=None)

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

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

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

compare_total(other, context=None)

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

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

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

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

compare_total_mag(other, context=None)

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

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

conjugate()

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

copy_abs()

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

copy_negate()

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

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')
from_float(f)

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

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

Примечание

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

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

Новое в версии 3.1.

fma(other, third, context=None)

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

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

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

is_finite()

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

is_infinite()

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

is_nan()

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

is_normal(context=None)

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

is_qnan()

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

is_signed()

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

is_snan()

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

is_subnormal(context=None)

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

is_zero()

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

ln(context=None)

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

log10(context=None)

Возвращает десятичный логарифм операнда. Результат правильно округляется с помощью режима округления 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)

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

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

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

to_integral(rounding=None, context=None)

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

to_integral_exact(rounding=None, context=None)

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

to_integral_value(rounding=None, context=None)

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

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

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

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

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

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

decimal.getcontext()

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

decimal.setcontext(c)

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

Также можно использовать оператор with и функцию localcontext() для временного изменения активного контекста.

decimal.localcontext(ctx=None)

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

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

from decimal import localcontext

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

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

class decimal.BasicContext

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

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

class decimal.ExtendedContext

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

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

class decimal.DefaultContext

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

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

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

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

Помимо трёх предоставленных контекстов, новые контексты могут быть созданы с помощью конструктора Context.

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

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

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

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

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

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

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

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

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

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

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

clear_flags()

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

clear_traps()

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

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

copy()

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

copy_decimal(num)

Возвращает копию экземпляра Decimal num.

create_decimal(num)

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

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

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

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

create_decimal_from_float(f)

Создает новый экземпляр Decimal из float 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)

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

Постоянные

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

Добавлена в версии 3.9: перезаписано для 3.7 и 3.8

END_OF_DOCUMENT_MARKER

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

decimal.ROUND_CEILING

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

decimal.ROUND_DOWN

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

decimal.ROUND_FLOOR

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

decimal.ROUND_HALF_DOWN

Округление к ближайшему значению с tie-break в сторону нуля.

decimal.ROUND_HALF_EVEN

Округление к ближайшему значению с tie-break к ближайшему чётному целому.

decimal.ROUND_HALF_UP

Округление к ближайшему значению с tie-break от нуля.

decimal.ROUND_UP

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

decimal.ROUND_05UP

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

Сигналы

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

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

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

class decimal.Clamped

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

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

class decimal.DecimalException

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

class decimal.DivisionByZero

Сигнализирует деление ненулевого числа на ноль.

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

class decimal.Inexact

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

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

class decimal.InvalidOperation

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

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

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

Переполнение чисел.

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

class decimal.Rounded

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

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

class decimal.Subnormal

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

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

class decimal.Underflow

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

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

class decimal.FloatOperation

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

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

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

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

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

Замечания по работе с плавающей точкой

Устранение ошибки округления с увеличенной точностью

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

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

# 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, -0, -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')]

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

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

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

Новая в версии 3.3.

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

Spec-Zone.ru

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