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

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


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

  • Decimal «основана на модели с плавающей точкой, которая была разработана с учетом интересов людей, и обязательно имеет главный руководящий принцип - компьютеры должны предоставлять арифметику, которая работает так же, как арифметика, которую люди изучают в школе.» – выдержка из спецификации десятичной арифметики.

  • Десятичные числа могут быть представлены точно. В отличие от этого, такие числа, как 1.1 и 2.2, не имеют точного представления в двоичной системе с плавающей запятой. Конечные пользователи обычно не ожидают, что 1.1 + 2.2 будет отображаться как 3.3000000000000003, как это происходит в двоичной системе с плавающей запятой.

  • Точность переносится в арифметику. В десятичной системе с плавающей запятой 0.1 + 0.1 + 0.1 - 0.3 точно равно нулю. В двоичной системе с плавающей запятой результат равен 5.5511151231257827e-017. Хотя разница близка к нулю, она препятствует надежной проверке равенства, и разница может накапливаться. По этой причине десятичная система предпочтительнее в бухгалтерских приложениях, где существуют строгие инварианты равенства.

  • Десятичный модуль включает понятие значимых мест, так что 1.30 + 1.20 является 2.50. Для обозначения значимости сохраняется идущий следом ноль. Это обычное представление для денежных приложений. Для умножения в «школьном учебнике» используются все цифры в множителях. Например, 1.3 * 1.2 дает 1.56, а 1.30 * 1.20 дает 1.5600.

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

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

Дизайн модуля сосредоточен вокруг трех понятий: десятичное число, контекст арифметики и сигналы.

Десятичное число неизменяемо. Оно имеет знак, цифры коэффициента и экспоненту. Чтобы сохранить значимость, цифры коэффициента не усекают нули в конце. Десятичные числа также включают специальные значения, такие как Infinity, -Infinity и NaN. Стандарт также отличает -0 от +0.

Контекст для арифметики - это среда, определяющая точность, правила округления, ограничения на экспоненты, флаги, указывающие на результаты операций, и ловушки, определяющие, будут ли сигналы рассматриваться как исключения. Варианты округления включают ROUND_CEILING, ROUND_DOWN, ROUND_FLOOR, ROUND_HALF_DOWN, ROUND_HALF_EVEN, ROUND_HALF_UP, ROUND_UP и ROUND_05UP.

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

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

См.также

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

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

>>> from decimal import *
>>> getcontext()
Context(prec=28, rounding=ROUND_HALF_EVEN, Emin=-999999, Emax=999999,
        capitals=1, clamp=0, flags=[], traps=[Overflow, DivisionByZero,
        InvalidOperation])

>>> getcontext().prec = 7       # Set a new precision

Десятичные экземпляры могут быть построены из целых чисел, строк, плавающих чисел или кортежей. При построении из целого числа или числа с плавающей точкой выполняется точное преобразование значения этого целого числа или числа с плавающей точкой. Десятичные числа включают специальные значения, такие как NaN, что означает «Не число», положительные и отрицательные Infinity, и -0:

>>> getcontext().prec = 28
>>> Decimal(10)
Decimal('10')
>>> Decimal('3.14')
Decimal('3.14')
>>> Decimal(3.14)
Decimal('3.140000000000000124344978758017532527446746826171875')
>>> Decimal((0, (3, 1, 4), -2))
Decimal('3.14')
>>> Decimal(str(2.0 ** 0.5))
Decimal('1.4142135623730951')
>>> Decimal(2) ** Decimal('0.5')
Decimal('1.414213562373095048801688724')
>>> Decimal('NaN')
Decimal('NaN')
>>> Decimal('-Infinity')
Decimal('-Infinity')

Если сигнал FloatOperation пойман, случайное смешивание десятичных и плавающих чисел в конструкторах или упорядочивающих сравнениях вызывает исключение:

>>> c = getcontext()
>>> c.traps[FloatOperation] = True
>>> Decimal(3.14)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
decimal.FloatOperation: [<class 'decimal.FloatOperation'>]
>>> Decimal('3.5') < 3.7
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
decimal.FloatOperation: [<class 'decimal.FloatOperation'>]
>>> Decimal('3.5') == 3.5
True

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

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

>>> getcontext().prec = 6
>>> Decimal('3.0')
Decimal('3.0')
>>> Decimal('3.1415926535')
Decimal('3.1415926535')
>>> Decimal('3.1415926535') + Decimal('2.7182818285')
Decimal('5.85987')
>>> getcontext().rounding = ROUND_UP
>>> Decimal('3.1415926535') + Decimal('2.7182818285')
Decimal('5.85988')

Если внутренние ограничения версии C превышены, построение десятичной дроби повышает InvalidOperation:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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'.

Если значение является tuple, то оно должно состоять из трех компонентов: знака (0 для положительных или 1 для отрицательных), tuple цифр и целочисленной экспоненты. Например, Decimal((0, (1, 4, 1, 4), -3)) возвращает Decimal('1.414').

Если значение является float, двоичное значение с плавающей точкой без потерь преобразуется в его точный десятичный эквивалент. Это преобразование часто может потребовать 53 или более цифр точности. Например, Decimal(float('1.1')) преобразуется в Decimal('1.100000000000000088817841970012523233890533447265625').

Точность контекста не влияет на то, сколько цифр будет храниться. Это определяется исключительно количеством цифр в значении. Например, Decimal('3.00000') записывает все пять нулей, даже если точность контекста равна только трем.

Цель аргумента context - определить, что делать, если value - неправильно сформированная строка. Если контекст ловит InvalidOperation, возникает исключение; в противном случае конструктор возвращает новый Decimal со значением NaN.

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

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

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

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

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

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

>>> (-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 (соответственно), как описано в спецификации.

Десятичные объекты обычно нельзя объединять с плавающей точкой или экземплярами fractions.Fraction в арифметических операциях: например, попытка добавить Decimal к float вызовет ошибку TypeError. Однако можно использовать операторы сравнения Python для сравнения экземпляра Decimal x с другим числом y. Это позволяет избежать путаницы при сравнении равенства между числами разных типов.

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

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

adjusted()

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

as_integer_ratio()

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

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

Преобразование является точным. Вызывает OverflowError при бесконечности и ValueError при NaNs.

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

as_tuple()

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

canonical()

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

compare(other, context=None)

Сравнивает значения двух экземпляров Decimal. compare() возвращает десятичный экземпляр, а если один из операндов является 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 Specification.

copy_abs()

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

copy_negate()

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

copy_sign(other, context=None)

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

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

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

exp(context=None)

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

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

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

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

Примечание

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

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

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

fma(other, third, context=None)

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

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

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

is_finite()

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

is_infinite()

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

is_nan()

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

is_normal(context=None)

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

is_qnan()

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

is_signed()

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

is_snan()

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

is_subnormal(context=None)

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

is_zero()

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

ln(context=None)

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

log10(context=None)

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

logb(context=None)

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

logical_and(other, context=None)

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

logical_invert(context=None)

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

logical_or(other, context=None)

logical_or() - это логическая операция, которая принимает два логических операнда (см. Логические операнды). Результатом является поразрядное отношение or двух операндов.

logical_xor(other, context=None)

logical_xor() - это логическая операция, которая принимает два логических операнда (см. Логические операнды). Результатом является поразрядное исключающее или из двух операндов.

max(other, context=None)

Подобно max(self, other) за исключением того, что перед возвратом применяется правило контекстного округления, а значения NaN либо сигнализируются, либо игнорируются (в зависимости от контекста и того, являются ли они сигнальными или тихими).

max_mag(other, context=None)

Аналогичен методу max(), но сравнение производится по абсолютным значениям операндов.

min(other, context=None)

Подобно min(self, other) за исключением того, что перед возвратом применяется правило контекстного округления, а значения NaN либо сигнализируются, либо игнорируются (в зависимости от контекста и того, являются ли они сигнальными или тихими).

min_mag(other, context=None)

Аналогичен методу min(), но сравнение производится по абсолютным значениям операндов.

next_minus(context=None)

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

next_plus(context=None)

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

next_toward(other, context=None)

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

normalize(context=None)

Нормализуйте число, удалив самые правые нули и преобразовав любой результат, равный 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 (Not a Number).

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

quantize(exp, rounding=None, context=None)

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

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

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

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

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

Ошибка возвращается, если полученная экспонента больше Emax или меньше Etiny.

radix()

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

remainder_near(other, context=None)

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

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

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

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)

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

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

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

Поле капитал имеет значение 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)

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

create_decimal(num)

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

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

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

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

create_decimal_from_float(f)

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

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

logical_xor(x, y)

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

max(x, y)

Сравнивает два значения численно и возвращает максимальное.

max_mag(x, y)

Сравнивает значения численно с игнорированием их знака.

min(x, y)

Сравнивает два значения численно и возвращает минимальное.

min_mag(x, y)

Сравнивает значения численно с игнорированием их знака.

minus(x)

Minus соответствует унарному префиксному оператору minus в 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)

Plus соответствует унарному префиксному оператору plus в Python. Эта операция применяет контекстную точность и округление, поэтому она не является операцией тождества.

power(x, y, modulo=None)

Возвращает x в степени y, уменьшенной по модулю modulo, если задано.

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

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

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

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

  • все три аргумента должны быть интегральными

  • y должен быть неотрицательным

  • хотя бы одно из x или y должно быть ненулевым

  • modulo должен быть ненулевым и иметь не более „точных“ цифр

Значение, полученное в результате Context.power(x, y, modulo), равно значению, которое было бы получено при вычислении (x**y) % modulo с неограниченной точностью, но вычисляется более эффективно. Экспонента результата равна нулю, независимо от экспонент x, y и modulo. Результат всегда точен.

quantize(x, y)

Возвращает значение, равное x (округленное), с экспонентой y.

radix()

Просто возвращает 10, так как это десятичная дробь, :)

remainder(x, y)

Возвращает остаток от целочисленного деления.

Знак результата, если он ненулевой, совпадает со знаком исходного дивиденда.

remainder_near(x, y)

Возвращает x - y * n, где n - целое число, ближайшее к точному значению x / y (если результат равен 0, то его знак будет знаком x).

rotate(x, y)

Возвращает повернутую копию x, y раз.

same_quantum(x, y)

Возвращает True, если два операнда имеют одинаковую экспоненту.

scaleb(x, y)

Возвращает первый операнд после добавления второго значения его exp.

shift(x, y)

Возвращает сдвинутую копию x, y раз.

sqrt(x)

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

subtract(x, y)

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

to_eng_string(x)

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

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

to_integral_exact(x)

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

to_sci_string(x)

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

Константы

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

32-бит

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 используется контекст thread-local, а не coroutine-local, и значение равно False. Это немного быстрее в некоторых сценариях с вложенными контекстами.

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

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

decimal.ROUND_CEILING

Круг в направлении Infinity.

decimal.ROUND_DOWN

Округляйте в сторону нуля.

decimal.ROUND_FLOOR

Круг в направлении -Infinity.

decimal.ROUND_HALF_DOWN

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

decimal.ROUND_HALF_EVEN

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

decimal.ROUND_HALF_UP

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

decimal.ROUND_UP

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

decimal.ROUND_05UP

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

Сигналы

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

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

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

class decimal.Clamped

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

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

class decimal.DecimalException

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

class decimal.DivisionByZero

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

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

class decimal.Inexact

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

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

class decimal.InvalidOperation

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

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

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

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

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

class decimal.Rounded

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

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

class decimal.Subnormal

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

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

class decimal.Underflow

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

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

class decimal.FloatOperation

Включите более строгую семантику для смешивания плавающих и десятичных чисел.

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

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

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

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

Заметки о плавающей точке

Устранение ошибок округления с повышением точности

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

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

# Examples from Seminumerical Algorithms, Section 4.2.2.
>>> from decimal import Decimal, getcontext
>>> getcontext().prec = 8

>>> u, v, w = Decimal(11111113), Decimal(-11111111), Decimal('7.51111111')
>>> (u + v) + w
Decimal('9.5111111')
>>> u + (v + w)
Decimal('10')

>>> u, v, w = Decimal(20000), Decimal(-6), Decimal('6.0000003')
>>> (u*v) + (u*w)
Decimal('0.01')
>>> u * (v+w)
Decimal('0.0060000')

Модуль decimal позволяет восстановить тождества, расширив точность настолько, чтобы избежать потери значимости:

>>> getcontext().prec = 20
>>> u, v, w = Decimal(11111113), Decimal(-11111111), Decimal('7.51111111')
>>> (u + v) + w
Decimal('9.51111111')
>>> u + (v + w)
Decimal('9.51111111')
>>>
>>> u, v, w = Decimal(20000), Decimal(-6), Decimal('6.0000003')
>>> (u*v) + (u*w)
Decimal('0.0060000')
>>> u * (v+w)
Decimal('0.0060000')

Особые значения

Система счисления для модуля decimal предусматривает специальные значения, включая NaN, sNaN, -Infinity, Infinity и два нуля, +0 и -0.

Бесконечности могут быть построены непосредственно с помощью: Decimal('Infinity'). Также они могут возникать при делении на ноль, если сигнал DivisionByZero не заперт. Аналогично, когда сигнал Overflow не заперт, бесконечность может возникнуть в результате округления за пределы наибольшего представимого числа.

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

Некоторые операции являются неопределенными и возвращают NaN, или, если сигнал InvalidOperation пойман, вызывают исключение. Например, 0/0 возвращает NaN, что означает «не число». Эта разновидность NaN является тихой и, будучи созданной, будет проходить через другие вычисления, всегда приводя к другому NaN. Такое поведение может быть полезно для серии вычислений, в которых иногда отсутствуют входные данные — оно позволяет продолжить вычисления, отмечая при этом определенные результаты как недопустимые.

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

Поведение операторов сравнения Python может быть немного удивительным, если в них участвует NaN. Проверка на равенство, когда одним из операндов является тихий или сигнальный NaN, всегда возвращает False (даже при выполнении Decimal('NaN')==Decimal('NaN')), а проверка на неравенство всегда возвращает True. Попытка сравнения двух десятичных дробей с помощью любого из операторов <, <=, > или >= вызовет сигнал InvalidOperation, если один из операндов является NaN, и вернет False, если этот сигнал не пойман. Обратите внимание, что спецификация General Decimal Arithmetic не определяет поведение прямых сравнений; эти правила для сравнений с участием NaN были взяты из стандарта IEEE 854 (см. таблицу 3 в разделе 5.7). Чтобы обеспечить строгое соответствие стандартам, используйте вместо них методы compare() и compare-signal().

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

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

>>> 1 / Decimal('Infinity')
Decimal('0E-1000026')

Работа с нитями

Функция getcontext() обращается к отдельному объекту Context для каждого потока. Наличие отдельных контекстов потоков означает, что потоки могут вносить изменения (например, getcontext().prec=10), не мешая другим потокам.

Аналогично, функция setcontext() автоматически назначает свою цель текущему потоку.

Если setcontext() не был вызван до getcontext(), то getcontext() автоматически создаст новый контекст для использования в текущем потоке.

Новый контекст копируется из прототипа контекста под названием DefaultContext. Чтобы управлять значениями по умолчанию так, чтобы каждый поток использовал одни и те же значения во всем приложении, непосредственно измените объект DefaultContext. Это должно быть сделано до запуска каких-либо потоков, чтобы не возникло состояния гонки между потоками, вызывающими getcontext(). Например:

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

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

Рецепты

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

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

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

    >>> d = Decimal('-1234567.8901')
    >>> moneyfmt(d, curr='$')
    '-$1,234,567.89'
    >>> moneyfmt(d, places=0, sep='.', dp='', neg='', trailneg='-')
    '1.234.568-'
    >>> moneyfmt(d, curr='$', neg='(', trailneg=')')
    '($1,234,567.89)'
    >>> moneyfmt(Decimal(123456789), sep=' ')
    '123 456 789.00'
    >>> moneyfmt(Decimal('-0.02'), neg='<', trailneg='>')
    '<0.02>'

    """
    q = Decimal(10) ** -places      # 2 places --> '0.01'
    sign, digits, exp = value.quantize(q).as_tuple()
    result = []
    digits = list(map(str, digits))
    build, next = result.append, digits.pop
    if sign:
        build(trailneg)
    for i in range(places):
        build(next() if digits else '0')
    if places:
        build(dp)
    if not digits:
        build('0')
    i = 0
    while digits:
        build(next())
        i += 1
        if i == 3 and digits:
            i = 0
            build(sep)
    build(curr)
    build(neg if sign else pos)
    return ''.join(reversed(result))

def pi():
    """Compute Pi to the current precision.

    >>> print(pi())
    3.141592653589793238462643383

    """
    getcontext().prec += 2  # extra digits for intermediate steps
    three = Decimal(3)      # substitute "three=3.0" for regular floats
    lasts, t, s, n, na, d, da = 0, three, 3, 1, 0, 0, 24
    while s != lasts:
        lasts = s
        n, na = n+na, na+8
        d, da = d+da, da+32
        t = (t * n) / d
        s += t
    getcontext().prec -= 2
    return +s               # unary plus applies the new precision

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

    >>> print(exp(Decimal(1)))
    2.718281828459045235360287471
    >>> print(exp(Decimal(2)))
    7.389056098930650227230427461
    >>> print(exp(2.0))
    7.38905609893
    >>> print(exp(2+0j))
    (7.38905609893+0j)

    """
    getcontext().prec += 2
    i, lasts, s, fact, num = 0, 0, 1, 1, 1
    while s != lasts:
        lasts = s
        i += 1
        fact *= i
        num *= x
        s += num / fact
    getcontext().prec -= 2
    return +s

def cos(x):
    """Return the cosine of x as measured in radians.

    The Taylor series approximation works best for a small value of x.
    For larger values, first compute x = x % (2 * pi).

    >>> print(cos(Decimal('0.5')))
    0.8775825618903727161162815826
    >>> print(cos(0.5))
    0.87758256189
    >>> print(cos(0.5+0j))
    (0.87758256189+0j)

    """
    getcontext().prec += 2
    i, lasts, s, fact, num, sign = 0, 0, 1, 1, 1, 1
    while s != lasts:
        lasts = s
        i += 2
        fact *= i * (i-1)
        num *= x * x
        sign *= -1
        s += num / fact * sign
    getcontext().prec -= 2
    return +s

def sin(x):
    """Return the sine of x as measured in radians.

    The Taylor series approximation works best for a small value of x.
    For larger values, first compute x = x % (2 * pi).

    >>> print(sin(Decimal('0.5')))
    0.4794255386042030002732879352
    >>> print(sin(0.5))
    0.479425538604
    >>> print(sin(0.5+0j))
    (0.479425538604+0j)

    """
    getcontext().prec += 2
    i, lasts, s, fact, num, sign = 1, 0, x, 1, x, 1
    while s != lasts:
        lasts = s
        i += 2
        fact *= i * (i-1)
        num *= x * x
        sign *= -1
        s += num / fact * sign
    getcontext().prec -= 2
    return +s

Вопросы и ответы по десятичной дроби

Q. Набирать decimal.Decimal('1234.5') очень обременительно. Есть ли способ минимизировать ввод текста при использовании интерактивного интерпретатора?

A. Некоторые пользователи сокращают конструктор до одной буквы:

>>> D = decimal.Decimal
>>> D('1.23') + D('3.45')
Decimal('4.68')

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

A. Метод 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

Q. Если у меня есть допустимые двухпозиционные входы, как мне сохранить этот инвариант во всем приложении?

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

Q. Существует множество способов выразить одно и то же значение. Числа 200, 200.000, 2E2 и 02E+4 имеют одно и то же значение с различной точностью. Есть ли способ преобразовать их к одному узнаваемому каноническому значению?

A. Метод 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')]

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

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

Q. Есть ли способ преобразовать обычное число float в Decimal?

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

>>> Decimal(math.pi)
Decimal('3.141592653589793115997963468544185161590576171875')

Q. Как я могу убедиться, что в сложном вычислении я не получил ложный результат из-за недостаточной точности или аномалий округления.

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

Q. Я заметил, что контекстная точность применяется к результатам операций, но не к входным данным. Есть ли что-то, чего следует остерегаться при смешивании значений с разной точностью?

A. Да. Принцип заключается в том, что все значения считаются точными, как и арифметические действия с этими значениями. Только результаты округляются. Преимущество ввода заключается в том, что «что введешь, то и получишь». Недостатком является то, что результаты могут выглядеть странно, если вы забудете, что входные данные не были округлены:

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

Q. Является ли реализация CPython быстрой для больших чисел?

A. Да. В реализациях CPython и PyPy3 версии C/CFFI модуля decimal интегрируют высокоскоростную библиотеку libmpdec для произвольной точности правильно округленной десятичной арифметики с плавающей точкой 1. libmpdec использует Karatsuba multiplication для средних чисел и Number Theoretic Transform для очень больших чисел.

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

Самый простой подход для опробования арифметики bignum - использовать максимальное значение для 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

В системах с overallocation (например, 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'>]

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

1

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

2

Изменено в версии 3.9: Теперь этот подход работает для всех точных результатов, кроме нецелых значений.

Вернуться на верх