Содержание страницы
9.4. decimal – Десятичная арифметика с фиксированной и плавающей точкой¶decimal – Decimal fixed point and floating point arithmetic
Модуль decimal обеспечивает поддержку быстрой и правильно округлённой десятичной арифметики с плавающей точкой. Он даёт несколько преимуществ по сравнению с типом данных float:
Decimal «основан на модели с плавающей запятой, разработанной с учетом человеческих потребностей, и имеет первостепенный руководящий принцип – компьютеры должны обеспечивать арифметику, работающую так же, как арифметика, которой люди учатся в школе». – отрывок из спецификации десятичной арифметики.
Десятичные числа могут быть представлены точно. В отличие от этого, такие числа, как 1.1 и 2.2, не имеют точного представления в двоичной арифметике с плавающей запятой. Конечные пользователи обычно не ожидают, что 1.1 + 2.2 будет отображаться как 3.3000000000000003, как это происходит в двоичной арифметике с плавающей запятой.
Точность сохраняется и в арифметике. В десятичной арифметике с плавающей точкой 0.1 + 0.1 + 0.1 - 0.3 точно равен нулю. В двоичной арифметике с плавающей точкой результат равен 5.5511151231257827e-017. Хотя это значение близко к нулю, различия мешают надёжной проверке на равенство, и ошибки могут накапливаться. По этой причине десятичная арифметика предпочтительна в бухгалтерских приложениях, где требуются строгие инварианты равенства.
Модуль decimal включает понятие значащих разрядов, так что 1.30 + 1.20 равно 2.50. Замыкающий ноль сохраняется для указания значимости. Это стандартное представление для финансовых приложений. Для умножения «школьный» подход использует все цифры множителей. Например, 1.3 * 1.2 даёт 1.56, а 1.30 * 1.20 даёт 1.5600.
В отличие от аппаратно реализованной двоичной арифметики с плавающей запятой, модуль decimal имеет настраиваемую точность (по умолчанию 28 знаков), которая может быть сколь угодно большой для данной задачи:
>>> from decimal import * >>> getcontext().prec = 6 >>> Decimal(1) / Decimal(7) Decimal('0.142857') >>> getcontext().prec = 28 >>> Decimal(1) / Decimal(7) Decimal('0.1428571428571428571428571429')
Как двоичная, так и десятичная арифметика с плавающей запятой реализованы в соответствии с опубликованными стандартами. В то время как встроенный тип float предоставляет лишь скромную часть своих возможностей, модуль decimal предоставляет все необходимые части стандарта. При необходимости программист имеет полный контроль над округлением и обработкой сигналов. Это включает возможность принудительного точного выполнения арифметики с помощью исключений для блокировки любых неточных операций.
Модуль decimal был разработан для поддержки «без каких-либо предпочтений как точной неокруглённой десятичной арифметики (иногда называемой арифметикой с фиксированной запятой), так и округлённой арифметики с плавающей запятой.» – выдержка из спецификации десятичной арифметики.
Дизайн модуля сосредоточен на трёх концепциях: десятичное число, контекст арифметики и сигналы.
Десятичное число неизменяемо. Оно имеет знак, цифры коэффициента и показатель степени. Чтобы сохранить значимость, цифры коэффициента не отбрасывают конечные нули. Десятичные числа также включают специальные значения, такие как Infinity, -Infinity и NaN. Стандарт также различает -0 и +0.
Контекст для арифметики – это среда, задающая точность, правила округления, ограничения на показатели степени, флаги, указывающие результаты операций, и разрешающие сигналы, которые определяют, обрабатываются ли сигналы как исключения. Варианты округления включают ROUND_CEILING, ROUND_DOWN, ROUND_FLOOR, ROUND_HALF_DOWN, ROUND_HALF_EVEN, ROUND_HALF_UP, ROUND_UP и ROUND_05UP.
Сигналы – это группы исключительных ситуаций, возникающих в ходе вычислений. В зависимости от потребностей приложения сигналы могут игнорироваться, рассматриваться как информационные или обрабатываться как исключения. Сигналы в модуле decimal: Clamped, InvalidOperation, DivisionByZero, Inexact, Rounded, Subnormal, Overflow, Underflow и FloatOperation.
Для каждого сигнала существует флаг и активатор ловушки. Когда сигнал обнаруживается, его флаг устанавливается в единицу, затем, если активатор ловушки установлен в единицу, возбуждается исключение. Флаги сохраняют состояние, поэтому пользователю необходимо сбрасывать их перед мониторингом вычислений.
См. также
- Спецификация общей десятичной арифметики IBM, Общая спецификация десятичной арифметики.
- Стандарт IEEE 854-1987, Неофициальный текст IEEE 854.
9.4.1. Краткое руководство¶Quick-start Tutorial
Обычно для работы с десятичными числами модуль импортируют, просматривают текущий контекст с помощью 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 # Установить новую точность
Экземпляры Decimal можно создавать из целых чисел, строк, чисел с плавающей точкой или кортежей. Создание из целого числа или числа с плавающей точкой выполняет точное преобразование значения этого целого числа или числа с плавающей точкой. Десятичные числа включают специальные значения, такие как NaN, что означает «Не число», положительную и отрицательную Infinity и -0:
>>> getcontext().prec = 28
>>> Decimal(10)
Decimal('10')
>>> Decimal('3.14')
Decimal('3.14')
>>> Decimal(3.14)
Decimal('3.140000000000000124344978758017532527446746826171875')
>>> Decimal((0, (3, 1, 4), -2))
Decimal('3.14')
>>> Decimal(str(2.0 ** 0.5))
Decimal('1.4142135623730951')
>>> Decimal(2) ** Decimal('0.5')
Decimal('1.414213562373095048801688724')
>>> Decimal('NaN')
Decimal('NaN')
>>> Decimal('-Infinity')
Decimal('-Infinity')
Если для сигнала FloatOperation установлена ловушка, случайное смешивание десятичных чисел и чисел с плавающей точкой в конструкторах или при сравнении порядка вызывает исключение:
>>> c = getcontext()
>>> c.traps[FloatOperation] = True
>>> Decimal(3.14)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
decimal.FloatOperation: [<class 'decimal.FloatOperation'>]
>>> Decimal('3.5') < 3.7
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
decimal.FloatOperation: [<class 'decimal.FloatOperation'>]
>>> Decimal('3.5') == 3.5
True
Новое в версии 3.3.
Значимость нового Decimal определяется исключительно количеством введённых цифр. Точность контекста и округление вступают в силу только во время арифметических операций.
>>> getcontext().prec = 6
>>> Decimal('3.0')
Decimal('3.0')
>>> Decimal('3.1415926535')
Decimal('3.1415926535')
>>> Decimal('3.1415926535') + Decimal('2.7182818285')
Decimal('5.85987')
>>> getcontext().rounding = ROUND_UP
>>> Decimal('3.1415926535') + Decimal('2.7182818285')
Decimal('5.85988')
Если превышены внутренние ограничения C-версии, создание десятичного числа вызывает InvalidOperation:
>>> Decimal("1e9999999999999999999")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
decimal.InvalidOperation: [<class 'decimal.InvalidOperation'>]
Изменено в версии 3.3.
Десятичные числа хорошо взаимодействуют с остальной частью Python. Вот небольшой пример разнообразия десятичной арифметики с плавающей точкой:
>>> data = list(map(Decimal, '1.34 1.87 3.45 2.35 1.00 0.03 9.25'.split()))
>>> max(data)
Decimal('9.25')
>>> min(data)
Decimal('0.03')
>>> sorted(data)
[Decimal('0.03'), Decimal('1.00'), Decimal('1.34'), Decimal('1.87'),
Decimal('2.35'), Decimal('3.45'), Decimal('9.25')]
>>> sum(data)
Decimal('19.29')
>>> a,b,c = data[:3]
>>> str(a)
'1.34'
>>> float(a)
1.34
>>> round(a, 1)
Decimal('1.3')
>>> int(a)
1
>>> a * 5
Decimal('6.70')
>>> a * b
Decimal('2.5058')
>>> c % a
Decimal('0.77')
Также некоторые математические функции доступны для Decimal:
>>> getcontext().prec = 28
>>> Decimal(2).sqrt()
Decimal('1.414213562373095048801688724')
>>> Decimal(1).exp()
Decimal('2.718281828459045235360287471')
>>> Decimal('10').ln()
Decimal('2.302585092994045684017991455')
>>> Decimal('10').log10()
Decimal('1')
Метод quantize() округляет число до заданного показателя степени. Этот метод полезен для денежных приложений, которые часто округляют результаты до фиксированного количества знаков:
>>> Decimal('7.325').quantize(Decimal('.01'), rounding=ROUND_DOWN)
Decimal('7.32')
>>> Decimal('7.325').quantize(Decimal('1.'), rounding=ROUND_UP)
Decimal('8')
Как показано выше, функция getcontext() обращается к текущему контексту и позволяет изменять настройки. Такой подход удовлетворяет потребности большинства приложений.
Для более сложной работы может быть полезно создавать альтернативные контексты с помощью конструктора Context(). Чтобы сделать альтернативный контекст активным, используйте функцию setcontext().
In accordance with the standard, the Decimal module provides two ready to use standard contexts, BasicContext and ExtendedContext. The former is especially useful for debugging because many of the traps are enabled:
>>> 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.
9.4.2. Объекты Decimal¶Decimal objects
- 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
Другие десятичные цифры Unicode также допускаются там, где выше указано digit. К ним относятся десятичные цифры из различных других алфавитов (например, арабо-индийские цифры и деванагари), а также полноширинные цифры от '\uff10' до '\uff19'.
Если value является tuple, он должен иметь три компонента: знак (0 для положительного или 1 для отрицательного), tuple цифр и целочисленный показатель степени. Например, Decimal((0, (1, 4, 1, 4), -3)) возвращает Decimal('1.414').
Если value является float, двоичное значение с плавающей точкой без потерь преобразуется в его точный десятичный эквивалент. Это преобразование часто может потребовать 53 или более цифр точности. Например, Decimal(float('1.1')) преобразуется в Decimal('1.100000000000000088817841970012523233890533447265625').
Точность context не влияет на количество сохраняемых цифр. Оно определяется исключительно количеством цифр в value. Например, Decimal('3.00000') сохраняет все пять нулей, даже если точность контекста равна только трём.
Назначение аргумента context – определить, что делать, если value является некорректной строкой. Если контекст перехватывает сигнал InvalidOperation, вызывается исключение; в противном случае конструктор возвращает новый Decimal со значением NaN.
После создания объекты Decimal являются неизменяемыми.
Изменено в версии 3.2: Теперь аргумент конструктора может быть экземпляром float.
Изменено в версии 3.3: Аргументы float вызывают исключение, если установлена ловушка FloatOperation. По умолчанию ловушка выключена.
Объекты Decimal имеют много общего с другими встроенными числовыми типами, такими как float и int. Для них определены все стандартные математические операции и специальные методы. Кроме того, десятичные объекты можно копировать, сериализовать (pickle), выводить на печать, использовать в качестве ключей словаря, элементов множества, сравнивать, сортировать и преобразовывать в другой тип (например, float или int).
Существуют небольшие различия между арифметикой над объектами Decimal и арифметикой над целыми числами и числами с плавающей запятой. Когда оператор остатка % применяется к объектам Decimal, знак результата совпадает со знаком dividend, а не со знаком делителя:
>>> (-7) % 4 1 >>> Decimal(-7) % Decimal(4) Decimal('-3')
Оператор целочисленного деления // ведёт себя аналогично: возвращает целую часть истинного частного (с округлением в сторону нуля), а не его пол, чтобы сохранить обычное тождество x == (x // y) * y + x % y:
>>> -7 // 4 -2 >>> Decimal(-7) // Decimal(4) Decimal('-1')
Операторы % и // реализуют операции остаток и целочисленное-деление (соответственно) в соответствии со спецификацией.
Объекты Decimal обычно нельзя комбинировать с числами float или экземплярами fractions.Fraction в арифметических операциях: попытка добавить Decimal к float, например, вызовет TypeError. Однако можно использовать операторы сравнения Python для сравнения экземпляра Decimal x с другим числом y. Это позволяет избежать путаницы при проверках на равенство между числами разных типов.
Изменено в версии 3.2: Сравнения смешанных типов между экземплярами Decimal и другими числовыми типами теперь полностью поддерживаются.
В дополнение к стандартным числовым свойствам десятичные объекты с плавающей точкой также имеют ряд специализированных методов:
- adjusted()¶
Возвращает скорректированный показатель степени после удаления крайних правых цифр коэффициента, пока не останется только старшая цифра: Decimal('321e+5').adjusted() возвращает семь. Используется для определения положения самой значащей цифры относительно десятичной точки.
- as_tuple()¶
Возвращает представление числа в виде именованного кортежа: DecimalTuple(sign, digits, exponent).
- canonical()¶
Возвращает каноническую кодировку аргумента. В настоящее время кодировка экземпляра Decimal всегда канонична, поэтому эта операция возвращает свой аргумент без изменений.
- compare(other, context=None)¶
Сравнивает значения двух экземпляров Decimal. compare() возвращает экземпляр Decimal, и если любой из операндов является NaN, то результатом будет NaN:
a or b is a NaN ==> Decimal('NaN') a < b ==> Decimal('-1') a == b ==> Decimal('0') a > b ==> Decimal('1')
- compare_signal(other, context=None)¶
Эта операция идентична методу compare(), за исключением того, что все NaN сигнализируют. То есть, если ни один операнд не является сигнальным NaN, то любой тихий NaN обрабатывается так, как если бы он был сигнальным NaN.
- compare_total(other, context=None)¶
Сравнивает два операнда, используя их абстрактное представление, а не числовое значение. Аналогично методу compare(), но результат даёт полное упорядочение экземпляров Decimal. Два экземпляра Decimal с одинаковым числовым значением, но разными представлениями сравниваются как неравные в этом упорядочении:
>>> Decimal('12.0').compare_total(Decimal('12')) Decimal('-1')
Тихие и сигнальные NaN также включены в полное упорядочение. Результат этой функции равен Decimal('0'), если оба операнда имеют одинаковое представление, Decimal('-1'), если первый операнд находится ниже в полном порядке, чем второй, и Decimal('1'), если первый операнд находится выше в полном порядке, чем второй операнд. Подробнее о полном порядке см. в спецификации.
Эта операция не зависит от контекста и является тихой: никакие флаги не меняются и округление не выполняется. Исключение составляет C-версия, которая может возбудить InvalidOperation, если второй операнд невозможно точно преобразовать.
- compare_total_mag(other, context=None)¶
Сравнивает два операнда, используя их абстрактное представление, а не их значение, как в compare_total(), но игнорируя знак каждого операнда. x.compare_total_mag(y) эквивалентно x.copy_abs().compare_total(y.copy_abs()).
Эта операция не зависит от контекста и является тихой: никакие флаги не меняются и округление не выполняется. Исключение составляет C-версия, которая может возбудить InvalidOperation, если второй операнд невозможно точно преобразовать.
- conjugate()¶
Просто возвращает self; этот метод существует только для соответствия спецификации Decimal.
- copy_abs()¶
Возвращает абсолютное значение аргумента. Эта операция не зависит от контекста и является тихой: флаги не изменяются и округление не выполняется.
- copy_negate()¶
Возвращает отрицание аргумента. Эта операция не зависит от контекста и является тихой: флаги не изменяются и округление не выполняется.
- copy_sign(other, context=None)¶
Возвращает копию первого операнда со знаком, установленным таким же, как знак второго операнда. Например:
>>> Decimal('2.3').copy_sign(Decimal('-1.5')) Decimal('-2.3')
Эта операция не зависит от контекста и является тихой: никакие флаги не меняются и округление не выполняется. Исключение составляет C-версия, которая может возбудить InvalidOperation, если второй операнд невозможно точно преобразовать.
- exp(context=None)¶
Возвращает значение (натуральной) экспоненциальной функции e**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.
>>> 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_signed()¶
Возвращает True, если аргумент имеет отрицательный знак, и False в противном случае. Обратите внимание, что нули и NaN могут иметь знак.
- 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() – логическая операция, принимающая два логических операнда (см. Логические операнды). Результатом является поразрядное И двух операндов.
- logical_invert(context=None)¶
logical_invert() – логическая операция. Результатом является поразрядная инверсия операнда.
- logical_or(other, context=None)¶
logical_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 (не числом).
- "sNaN" – указывает, что операнд является сигнальным NaN.
- quantize(exp, rounding=None, context=None, watchexp=True)¶
Возвращает значение, равное первому операнду после округления и имеющее экспоненту второго операнда.
>>> Decimal('1.41421356').quantize(Decimal('1.000')) Decimal('1.414')
В отличие от других операций, если длина коэффициента после операции quantize превышает точность, то сигнализируется InvalidOperation. Это гарантирует, что при отсутствии ошибки квантованный порядок всегда равен порядку правого операнда.
Также, в отличие от других операций, quantize никогда не сигнализирует Underflow, даже если результат субнормален и неточен.
Если порядок второго операнда больше порядка первого, то может потребоваться округление. В этом случае режим округления определяется аргументом rounding, если он задан, иначе – аргументом context; если не задан ни один аргумент, используется режим округления контекста текущего потока.
Если watchexp установлен (по умолчанию), то ошибка возвращается всякий раз, когда результирующий порядок больше Emax или меньше Etiny.
Устарело с версии 3.3: watchexp – деталь реализации из версии на чистом Python и отсутствует в версии на C. Будет удалено в версии 3.4, где по умолчанию True.
- radix()¶
Возвращает Decimal(10) – основание системы счисления (базу), в которой класс Decimal выполняет все свои арифметические операции. Включён для совместимости со спецификацией.
- remainder_near(other, context=None)¶
Возвращает остаток от деления self на other. Отличается от self % other тем, что знак остатка выбирается так, чтобы минимизировать его абсолютное значение. Точнее, возвращаемое значение равно self - n * other, где n – целое число, ближайшее к точному значению self / other, а если два целых числа одинаково близки, то выбирается чётное.
Если результат равен нулю, его знак будет знаком self.
>>> Decimal(18).remainder_near(Decimal(10)) Decimal('-2') >>> Decimal(25).remainder_near(Decimal(10)) Decimal('5') >>> Decimal(35).remainder_near(Decimal(10)) Decimal('-5')
- rotate(other, context=None)¶
Возвращает результат вращения цифр первого операнда на величину, заданную вторым операндом. Второй операнд должен быть целым числом в диапазоне от -precision до precision. Абсолютное значение второго операнда задаёт количество позиций для вращения. Если второй операнд положителен, вращение выполняется влево; в противном случае вращение выполняется вправо. Коэффициент первого операнда при необходимости дополняется слева нулями до длины precision. Знак и экспонента первого операнда не изменяются.
- same_quantum(other, context=None)¶
Проверяет, имеют ли self и other одинаковую экспоненту или оба являются NaN.
Эта операция не зависит от контекста и является тихой: никакие флаги не меняются и округление не выполняется. Исключение составляет C-версия, которая может возбудить InvalidOperation, если второй операнд невозможно точно преобразовать.
- scaleb(other, context=None)¶
Возвращает первый операнд с экспонентой, скорректированной на второй. Эквивалентно умножению первого операнда на 10**other. Второй операнд должен быть целым числом.
- shift(other, context=None)¶
Возвращает результат сдвига цифр первого операнда на величину, заданную вторым операндом. Второй операнд должен быть целым числом в диапазоне от -precision до precision. Абсолютное значение второго операнда задаёт количество позиций для сдвига. Если второй операнд положителен, сдвиг выполняется влево; в противном случае сдвиг выполняется вправо. Вдвигаемые в коэффициент цифры являются нулями. Знак и экспонента первого операнда не изменяются.
- sqrt(context=None)¶
Возвращает квадратный корень аргумента с полной точностью.
- to_eng_string(context=None)¶
Преобразует в строку инженерного типа.
В инженерной нотации показатель степени кратен 3, поэтому слева от десятичной запятой может быть до 3 цифр. Например, преобразует Decimal('123E+1') в Decimal('1.23E+3')
- to_integral(rounding=None, context=None)¶
Идентичен методу to_integral_value(). Имя to_integral сохранено для совместимости со старыми версиями.
- to_integral_exact(rounding=None, context=None)¶
Округляет до ближайшего целого, сигнализируя Inexact или Rounded в зависимости от того, произошло ли округление. Режим округления определяется параметром rounding, если он задан, иначе – заданным context. Если ни один параметр не задан, используется режим округления текущего контекста.
9.4.3. Объекты контекста¶Context objects
Контексты – это среды для арифметических операций. Они управляют точностью, задают правила округления, определяют, какие сигналы обрабатываются как исключения, и ограничивают диапазон для экспонент.
Каждый поток имеет свой собственный текущий контекст, доступ к которому осуществляется или изменяется с помощью функций 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 # Выполнить вычисление с высокой точностью s = calculate_something() s = +s # Округлить конечный результат до стандартной точности
Новые контексты также можно создавать с помощью конструктора Context, описанного ниже. Кроме того, модуль предоставляет три готовых контекста:
- class decimal.BasicContext¶
Это стандартный контекст, определённый в спецификации General Decimal Arithmetic. Точность установлена в девять. Режим округления – ROUND_HALF_UP. Все флаги сброшены. Все ловушки включены (обрабатываются как исключения), кроме Inexact, Rounded и Subnormal.
Поскольку многие ловушки включены, этот контекст полезен для отладки.
- class decimal.ExtendedContext¶
Это стандартный контекст, определённый в спецификации General Decimal Arithmetic. Точность установлена в девять. Режим округления – ROUND_HALF_EVEN. Все флаги сброшены. Никакие ловушки не включены (чтобы во время вычислений не возникали исключения).
Поскольку ловушки отключены, этот контекст полезен для приложений, которые предпочитают получать значение NaN или Infinity вместо вызова исключений. Это позволяет приложению завершить выполнение при наличии условий, которые в противном случае остановили бы программу.
- class decimal.DefaultContext¶
Этот контекст используется конструктором Context в качестве прототипа для новых контекстов. Изменение поля (например, точности) меняет значение по умолчанию для новых контекстов, создаваемых конструктором Context.
Этот контекст наиболее полезен в многопоточных средах. Изменение одного из полей до запуска потоков приводит к установке общесистемных значений по умолчанию. Изменять поля после запуска потоков не рекомендуется, так как это потребовало бы синхронизации потоков для предотвращения состояний гонки.
В однопоточных средах лучше вообще не использовать этот контекст. Вместо этого просто создавайте контексты явно, как описано ниже.
Значения по умолчанию: 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 содержат список сигналов, которые необходимо установить. Как правило, для новых контекстов следует задавать только ловушки (traps), а флаги (flags) оставлять сброшенными.
Поля Emin и Emax – целые числа, задающие допустимые внешние границы для показателей степени. Emin должно быть в диапазоне [MIN_EMIN, 0], а Emax – в диапазоне [0, MAX_EMAX].
Поле capitals может быть равно 0 или 1 (по умолчанию). Если установлено в 1, показатели степени выводятся с заглавной E; в противном случае используется строчная e: Decimal('6.02e+23').
Поле clamp может быть равно 0 (по умолчанию) или 1. Если установлено в 1, показатель степени e экземпляра Decimal, представимого в этом контексте, строго ограничивается диапазоном Emin - prec + 1 <= e <= Emax - prec + 1. Если clamp равно 0, то выполняется более слабое условие: скорректированный показатель степени экземпляра Decimal не превышает Emax. Когда clamp равно 1, большое нормальное число, по возможности, уменьшает свой показатель степени и добавляет соответствующее количество нулей к коэффициенту, чтобы соблюсти ограничения на показатель степени; это сохраняет значение числа, но теряет информацию о значимых конечных нулях. Например:
>>> Context(prec=6, Emax=999, clamp=1).create_decimal('1.23e999') Decimal('1.23000E+999')
Значение clamp, равное 1, обеспечивает совместимость с форматами обмена десятичными числами фиксированной ширины, определёнными в IEEE 754.
Класс Context определяет несколько методов общего назначения, а также большое количество методов для непосредственного выполнения арифметических операций в заданном контексте. Кроме того, для каждого из методов Decimal, описанных выше (за исключением методов adjusted() и as_tuple()), существует соответствующий метод Context. Например, для экземпляра Context C и экземпляра Decimal x вызов C.exp(x) эквивалентен x.exp(context=C). Каждый метод Context принимает целое число Python (экземпляр int) везде, где ожидается экземпляр Decimal.
- clear_flags()¶
Сбрасывает все флаги в 0.
- clear_traps()¶
Сбрасывает все ловушки в 0.
Новое в версии 3.3.
- copy()¶
Возвращает копию контекста.
- copy_decimal(num)¶
Возвращает копию экземпляра Decimal num.
- create_decimal(num)¶
Создаёт новый экземпляр Decimal из num, используя self в качестве контекста. В отличие от конструктора Decimal, при преобразовании применяются точность контекста, метод округления, флаги и ловушки.
Это полезно, поскольку константы часто задаются с большей точностью, чем требуется приложению. Ещё одно преимущество в том, что округление немедленно устраняет нежелательные эффекты от цифр за пределами текущей точности. В следующем примере использование неокруглённых входных данных означает, что добавление нуля к сумме может изменить результат:
>>> getcontext().prec = 3 >>> Decimal('3.4445') + Decimal('1.0023') Decimal('4.45') >>> Decimal('3.4445') + Decimal(0) + Decimal('1.0023') Decimal('4.44')
Этот метод реализует операцию преобразования в число из спецификации IBM. Если аргумент – строка, начальные и конечные пробелы не допускаются.
- create_decimal_from_float(f)¶
Создаёт новый экземпляр Decimal из числа с плавающей запятой f, выполняя округление с использованием self в качестве контекста. В отличие от метода класса Decimal.from_float(), при преобразовании применяются точность контекста, метод округления, флаги и ловушки.
>>> context = Context(prec=5, rounding=ROUND_DOWN) >>> context.create_decimal_from_float(math.pi) Decimal('3.1415') >>> context = Context(prec=5, traps=[Inexact]) >>> context.create_decimal_from_float(math.pi) Traceback (most recent call last): ... decimal.Inexact: None
Новое в версии 3.1.
- Etiny()¶
Возвращает значение, равное Emin - prec + 1, которое является минимальным значением показателя степени для субнормальных результатов. В случае антипереполнения (underflow) показатель степени устанавливается равным 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)¶
Возвращает десятичный логарифм x.
- logb(x)¶
Возвращает показатель степени величины старшего разряда операнда.
- logical_and(x, y)¶
Применяет логическую операцию and к соответствующим цифрам операндов.
- logical_invert(x)¶
Инвертирует все цифры в x.
- logical_or(x, y)¶
Применяет логическую операцию or к соответствующим цифрам операндов.
- logical_xor(x, y)¶
Применяет логическую операцию xor к соответствующим цифрам операндов.
- max(x, y)¶
Сравнивает два числа и возвращает наибольшее.
- max_mag(x, y)¶
Сравнивает числа по модулю (игнорируя знак).
- min(x, y)¶
Сравнивает два числа и возвращает наименьшее.
- min_mag(x, y)¶
Сравнивает числа по модулю (игнорируя знак).
- minus(x)¶
Минус соответствует унарному префиксному оператору минус в Python.
- multiply(x, y)¶
Возвращает произведение x и y.
- next_minus(x)¶
Возвращает наибольшее представимое число, меньшее чем x.
- next_plus(x)¶
Возвращает наименьшее представимое число, большее чем x.
- next_toward(x, y)¶
Возвращает число, ближайшее к x в направлении к y.
- normalize(x)¶
Приводит x к простейшей форме.
- number_class(x)¶
Возвращает указание на класс x.
- plus(x)¶
Plus соответствует унарному префиксному оператору плюс в Python. Эта операция применяет точность и округление контекста, поэтому она не является тождественной операцией.
- power(x, y, modulo=None)¶
Возвращает x, возведённое в степень y, с приведением по модулю modulo, если он задан.
С двумя аргументами вычисляется x**y. Если x отрицательно, то y должно быть целым. Результат будет неточным, за исключением случаев, когда y является целым, а результат – конечным и может быть точно выражен в 'precision' цифрах. Используется режим округления контекста. В версии Python результаты всегда правильно округлены.
Изменено в версии 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)¶
Преобразует число в строку, используя научную нотацию.
- to_integral_exact(x)¶
Округляет до целого.
- to_sci_string(x)¶
Преобразует число в строку, используя научную запись.
9.4.4. Константы¶Constants
Константы в этом разделе имеют значение только для модуля C. Они также включены в чистую версию Python для совместимости.
| 32-битный | 64-битный | |
|---|---|---|
|
425000000 | 999999999999999999 |
|
425000000 | 999999999999999999 |
|
-425000000 | -999999999999999999 |
|
-849999999 | -1999999999999999997 |
- decimal.HAVE_THREADS¶
Значение по умолчанию – True. Если Python скомпилирован без поддержки потоков, C-версия автоматически отключает дорогостоящий механизм локального контекста потока. В этом случае значение равно False.
9.4.5. Режимы округления¶Rounding modes
- 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; в противном случае округление к нулю.
9.4.6. Сигналы¶Signals
Сигналы представляют условия, возникающие во время вычислений. Каждый соответствует одному флагу контекста и одному включателю ловушки контекста.
Флаг контекста устанавливается при возникновении условия. После вычисления флаги могут быть проверены в информационных целях (например, чтобы определить, было ли вычисление точным). После проверки флагов необходимо очистить все флаги перед началом следующего вычисления.
Если для сигнала включён ловушечный переключатель контекста, то условие вызывает возбуждение исключения Python. Например, если установлена ловушка DivisionByZero, то при возникновении условия возбуждается исключение DivisionByZero.
- class decimal.Clamped¶
Изменён показатель степени для соответствия ограничениям представления.
Обычно усечение (clamping) происходит, когда показатель степени выходит за пределы контекста 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¶
Exponent was lower than Emin prior to rounding.
Возникает, когда результат операции является субнормальным (экспонента слишком мала). Если не перехвачено, возвращает результат без изменений.
- class decimal.Underflow¶
Числовое исчерпание с округлением результата до нуля.
Occurs when a subnormal result is pushed to zero by rounding. Inexact and Subnormal are also signaled.
- class decimal.FloatOperation¶
Включает более строгую семантику смешивания чисел с плавающей запятой и десятичных чисел.
If the signal is not trapped (default), mixing floats and Decimals is permitted in the Decimal constructor, create_decimal() and all comparison operators. Both conversion and comparisons are exact. Any occurrence of a mixed operation is silently recorded by setting FloatOperation in the context flags. Explicit conversions with from_float() or create_decimal_from_float() do not set the flag.
Otherwise (the signal is trapped), only equality comparisons and explicit conversions are silent. All other mixed operations raise 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)
9.4.7. Замечания по числам с плавающей запятой¶Floating Point Notes
9.4.7.1. Уменьшение ошибки округления с помощью повышенной точности¶Mitigating round-off error with increased precision
Использование десятичной арифметики с плавающей запятой устраняет ошибку десятичного представления (позволяя точно представить 0.1); однако некоторые операции всё ещё могут приводить к ошибке округления, когда ненулевые цифры превышают фиксированную точность.
Влияние ошибки округления может усиливаться при сложении или вычитании почти компенсирующих друг друга величин, что приводит к потере значимости. Кнут приводит два поучительных примера, в которых округлённая арифметика с плавающей точкой с недостаточной точностью приводит к нарушению ассоциативного и дистрибутивного свойств сложения:
# Примеры из Seminumerical Algorithms, раздел 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')
The decimal module makes it possible to restore the identities by expanding the precision sufficiently to avoid loss of significance:
>>> 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')
9.4.7.2. Особые значения¶Special values
The number system for the decimal module provides special values including NaN, sNaN, -Infinity, Infinity, and two zeros, +0 and -0.
Infinities can be constructed directly with: Decimal('Infinity'). Also, they can arise from dividing by zero when the DivisionByZero signal is not trapped. Likewise, when the Overflow signal is not trapped, infinity can result from rounding beyond the limits of the largest representable number.
Бесконечности являются знаковыми (аффинными) и могут использоваться в арифметических операциях, где они трактуются как очень большие неопределённые числа. Например, прибавление константы к бесконечности даёт другой бесконечный результат.
Some operations are indeterminate and return NaN, or if the InvalidOperation signal is trapped, raise an exception. For example, 0/0 returns NaN which means “not a number”. This variety of NaN is quiet and, once created, will flow through other computations always resulting in another NaN. This behavior can be useful for a series of computations that occasionally have missing inputs – it allows the calculation to proceed while flagging specific results as invalid.
A variant is sNaN which signals rather than remaining quiet after every operation. This is a useful return value when an invalid result needs to interrupt a calculation for special handling.
The behavior of Python’s comparison operators can be a little surprising where a NaN is involved. A test for equality where one of the operands is a quiet or signaling NaN always returns False (even when doing Decimal('NaN')==Decimal('NaN')), while a test for inequality always returns True. An attempt to compare two Decimals using any of the <, <=, > or >= operators will raise the InvalidOperation signal if either operand is a NaN, and return False if this signal is not trapped. Note that the General Decimal Arithmetic specification does not specify the behavior of direct comparisons; these rules for comparisons involving a NaN were taken from the IEEE 854 standard (see Table 3 in section 5.7). To ensure strict standards-compliance, use the compare() and compare-signal() methods instead.
Знаковые нули могут возникать в результате вычислений, приводящих к исчерпанию. Они сохраняют знак, который получился бы, если бы вычисления выполнялись с большей точностью. Поскольку их величина равна нулю, положительный и отрицательный нули считаются равными, а их знак является информационным.
Помимо двух знаковых нулей, которые различны, но равны, существуют различные представления нуля с разной точностью, но эквивалентные по значению. К этому нужно немного привыкнуть. Для глаза, привыкшего к нормализованным представлениям чисел с плавающей точкой, не сразу очевидно, что следующее вычисление возвращает значение, равное нулю:
>>> 1 / Decimal('Infinity')
Decimal('0E-1000026')
9.4.8. Работа с потоками¶Working with threads
The getcontext() function accesses a different Context object for each thread. Having separate thread contexts means that threads may make changes (such as getcontext().prec=10) without interfering with other threads.
Likewise, the setcontext() function automatically assigns its target to the current thread.
If setcontext() has not been called before getcontext(), then getcontext() will automatically create a new context for use in the current thread.
The new context is copied from a prototype context called DefaultContext. To control the defaults so that each thread will use the same values throughout the application, directly modify the DefaultContext object. This should be done before any threads are started so that there won’t be a race condition between threads calling getcontext(). For example:
# Установить глобальные настройки по умолчанию для всех потоков, которые будут запущены
DefaultContext.prec = 12
DefaultContext.rounding = ROUND_DOWN
DefaultContext.traps = ExtendedContext.traps.copy()
DefaultContext.traps[InvalidOperation] = 1
setcontext(DefaultContext)
# После этого потоки можно запускать
t1.start()
t2.start()
t3.start()
. . .
9.4.9. Рецепты¶Recipes
Here are a few recipes that serve as utility functions and that demonstrate ways to work with the Decimal class:
def moneyfmt(value, places=2, curr='', sep=',', dp='.',
pos='', neg='-', trailneg=''):
"""Преобразовать Decimal в строку, отформатированную как денежная сумма.
places: обязательное количество знаков после десятичной точки
curr: необязательный символ валюты перед знаком (может быть пустым)
sep: необязательный разделитель групп (запятая, точка, пробел или пусто)
dp: указатель десятичной точки (запятая или точка)
указывать пустым только при places=0
pos: необязательный знак для положительных чисел: '+', пробел или пусто
neg: необязательный знак для отрицательных чисел: '-', '(', пробел или пусто
trailneg:необязательный завершающий знак минуса: '-', ')', пробел или пусто
>>> 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 знака после запятой --> '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():
"""Вычислить число Пи с текущей точностью.
>>> print(pi())
3.141592653589793238462643383
"""
getcontext().prec += 2 # дополнительные цифры для промежуточных шагов
three = Decimal(3) # подставить "three=3.0" вместо обычных чисел с плавающей точкой
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 # унарный плюс применяет новую точность
def exp(x):
"""Возвращает e, возведённое в степень x. Тип результата соответствует типу входного значения.
>>> 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):
"""Возвращает косинус x, измеренного в радианах.
Аппроксимация рядом Тейлора даёт наилучший результат при малых значениях x.
Для больших значений сначала вычислите 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):
"""Возвращает синус угла x, заданного в радианах.
Аппроксимация рядом Тейлора даёт наилучший результат при малых значениях x.
Для больших значений сначала вычислите 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
9.4.10. Часто задаваемые вопросы о Decimal¶Decimal FAQ
Q. It is cumbersome to type decimal.Decimal('1234.5'). Is there a way to minimize typing when using the interactive interpreter?
Ответ. Некоторые пользователи сокращают конструктор до одной буквы:
>>> D = decimal.Decimal
>>> D('1.23') + D('3.45')
Decimal('4.68')
Вопрос. В приложении с фиксированной запятой с двумя десятичными знаками некоторые входные данные имеют много знаков и нуждаются в округлении. Другие не должны содержать лишних цифр и требуют проверки. Какие методы следует использовать?
A. The quantize() method rounds to a fixed number of decimal places. If the Inexact trap is set, it is also useful for validation:
>>> TWOPLACES = Decimal(10) ** -2 # то же, что Decimal('0.01')
>>> # Округлить до двух знаков
>>> Decimal('3.214').quantize(TWOPLACES)
Decimal('3.21')
>>> # Проверка, что число не выходит за пределы двух знаков
>>> 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
Вопрос. Получив корректные входные данные с двумя знаками, как поддерживать этот инвариант во всём приложении?
A. Some operations like addition, subtraction, and multiplication by an integer will automatically preserve fixed point. Others operations, like division and non-integer multiplication, will change the number of decimal places and need to be followed-up with a quantize() step:
>>> a = Decimal('102.72') # Начальные значения чисел с фиксированной точкой
>>> b = Decimal('3.17')
>>> a + b # Сложение сохраняет фиксированную точку
Decimal('105.89')
>>> a - b
Decimal('99.55')
>>> a * 42 # Умножение на целое число – тоже
Decimal('4314.24')
>>> (a * b).quantize(TWOPLACES) # Результат умножения на нецелое число нужно квантовать
Decimal('325.62')
>>> (b / a).quantize(TWOPLACES) # И квантовать результат деления
Decimal('0.03')
In developing fixed-point applications, it is convenient to define functions to handle the quantize() step:
>>> 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) # Автоматически сохранять фиксированную точку
Decimal('325.62')
>>> div(b, a)
Decimal('0.03')
Q. There are many ways to express the same value. The numbers 200, 200.000, 2E2, and 02E+4 all have the same value at various precisions. Is there a way to transform them to a single recognizable canonical value?
A. The normalize() method maps all equivalent values to a single representative:
>>> values = map(Decimal, '200 200.000 2E2 .02E+4'.split())
>>> [v.normalize() for v in values]
[Decimal('2E+2'), Decimal('2E+2'), Decimal('2E+2'), Decimal('2E+2')]
Вопрос. Некоторые значения decimal всегда выводятся в экспоненциальной записи. Есть ли способ получить неэкспоненциальное представление?
A. For some values, exponential notation is the only way to express the number of significant places in the coefficient. For example, expressing 5.0E+3 as 5000 keeps the value constant but cannot show the original’s two-place significance.
Если приложению не важно отслеживать значимость, можно легко удалить экспоненту и хвостовые нули, потеряв значимость, но сохранив значение:
>>> 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. Is there a way to convert a regular float to a Decimal?
A. Да, любое двоичное число с плавающей точкой может быть точно выражено как 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') # Унарный плюс вызывает округление
Decimal('1.23')
Alternatively, inputs can be rounded upon creation using the Context.create_decimal() method:
>>> Context(prec=5, rounding=ROUND_DOWN).create_decimal('1.2345678')
Decimal('1.2345')