Модуль Decimal в Python: быстрая арифметика и округление
В этом уроке вы узнаете о модуле Decimal в Python, который поддерживает быструю арифметику десятичных чисел с плавающей запятой и правильным округлением.
- Введение в десятичный модуль Python
- Десятичный контекст
- Десятичный конструктор Decimal
- Десятичные арифметические операции
Введение в десятичный модуль Python
Многие десятичные числа не имеют точного представления в двоичном формате с плавающей запятой, например 0,1. Используя эти числа в арифметических операциях, вы получите результат, которого не ожидали. Например:
x = 0.1 y = 0.1 z = 0.1 s = x + y + z print(s)
Выход:
0.30000000000000004
Результат 0,30000000000000004, а не 0,3.
Чтобы решить эту проблему, используйте класс Decimal из модуля decimal следующим образом:
import decimal from decimal import Decimal x = Decimal('0.1') y = Decimal('0.1') z = Decimal('0.1') s = x + y + z print(s)
Выход:
0.3
Результат соответствует ожиданиям.
Десятичный модуль Python поддерживает арифметику, которая работает так же, как арифметика, которую вы изучаете в школе.
В отличие от чисел с плавающей запятой, Python точно представляет десятичные числа. И точность переносится в арифметику. Например, следующее выражение возвращает ровно 0,0:
Decimal('0.1') + Decimal('0.1') + Decimal('0.1') - Decimal('0.3')
Десятичный контекст
Десятичное число всегда связано с контекстом, который контролирует следующие аспекты:
- Точность во время арифметической операции
- Алгоритм округления
По умолчанию контекст является глобальным. Глобальный контекст является контекстом по умолчанию. Кроме того, вы можете установить временный контекст, который будет действовать локально, не затрагивая глобальный контекст.
Чтобы получить контекст по умолчанию, вы вызываете функцию getcontext() из decimal модуля:
decimal.getcontext()
Функция getcontext() возвращает контекст по умолчанию, который может быть глобальным или локальным.
Чтобы создать новый контекст, скопированный из другого, используйте функцию localcontext():
decimal.localcontext(ctx=None)
localcontext() возвращает новый контекст, скопированный из контекста ctx, если он указан.
Получив объект контекста, вы можете получить доступ к точности и маршрутизации через свойства prec и rounding соответственно:
- ctx.pre: получить или установить точность. ctx.pre — это целое число, которое по умолчанию равно 28.
- ctx.rounding: получить или установить механизм округления. Округление представляет собой строку. По умолчанию установлено значение «ROUND_HALF_EVEN».
Python предоставляет следующие механизмы округления:
Округление | Описание |
---|---|
ROUND_UP | округлить от нуля |
ROUND_DOWN | округлить до нуля |
ROUND_CEILING | округляется до потолка (в сторону положительной бесконечности) |
ROUND_FLOOR | округляется до пола (в сторону отрицательной бесконечности) |
ROUND_HALF_UP | округляется до ближайшего значения, от нуля |
ROUND_HALF_DOWN | до ближайшего значения, до нуля |
ROUND_HALF_EVEN | округление до ближайшего значения, привязка к четному(наименьшая значащая цифра) |
В этом примере показано, как получить точность и округление по умолчанию для контекста по умолчанию:
import decimal ctx = decimal.getcontext() print(ctx.prec) print(ctx.rounding)
Выход:
28 ROUND_HALF_EVEN
В следующем примере показано, как действует механизм округления ROUND_HALF_EVEN:
import decimal from decimal import Decimal x = Decimal('2.25') y = Decimal('3.35') print(round(x, 1)) print(round(y, 1))
Выход:
2.2 3.4
Если вы измените округление на «ROUND_HALF_UP», вы получите другой результат:
import decimal from decimal import Decimal ctx = decimal.getcontext() ctx.rounding = decimal.ROUND_HALF_UP x = Decimal('2.25') y = Decimal('3.35') print(round(x, 1)) print(round(y, 1))
Выход:
2.3 3.4
В следующем примере показано, как скопировать контекст по умолчанию и изменить округление на «ROUND_HALF_UP»:
import decimal from decimal import Decimal x = Decimal('2.25') y = Decimal('3.35') with decimal.localcontext() as ctx: print('Local context:') ctx.rounding = decimal.ROUND_HALF_UP print(round(x, 1)) print(round(y, 1)) print('Global context:') print(round(x, 1)) print(round(y, 1))
Выход:
Local context: 2.3 3.4 Global context: 2.2 3.4
Обратите внимание, что локальный контекст не влияет на глобальный контекст. После блока with Python использует механизм округления по умолчанию.
Десятичный конструктор Decimal
Конструктор Decimal позволяет вам создать новый объект Decimal на основе значения:
Decimal(value='0', context=None)
Аргументом value может быть целое число, строка, кортеж, число с плавающей запятой или другой объект Decimal. Если вы не укажете аргумент value, по умолчанию он будет равен «0».
Если значение представляет собой кортеж, оно должно состоять из трех компонентов: знака (0 для положительного значения или 1 для отрицательного), кортежа цифр и целочисленного показателя степени:
(sign,(digit1,digit2, digit3,...), exponent)
Например:
3.14 = 314 x 10^-2
Кортеж состоит из трех элементов:
- знак (0)
- цифры (3,1,4)
- показатель степени (-2)
Поэтому вам нужно будет передать в конструктор Decimal следующий кортеж:
import decimal from decimal import Decimal x = Decimal((0,(3, 1, 4), -2)) print(x)
Выход:
3.14
Обратите внимание, что точность десятичного контекста влияет только на арифметическую операцию, а не на конструктор Decimal. Например:
import decimal from decimal import Decimal decimal.getcontext().prec = 2 pi = Decimal('3.14159') radius = 1 print(pi) area = pi * radius * radius print(area)
Когда вы используете число с плавающей запятой, которое не имеет точного двоичного представления с плавающей запятой, конструктор Decimal не может создать точное десятичное представление. Например:
import decimal from decimal import Decimal x = Decimal(0.1) print(x)
Выход:
0.1000000000000000055511151231257827021181583404541015625
На практике для создания десятичного числа вы будете использовать строку или кортеж.
Десятичные арифметические операции
Некоторые арифметические операторы работают не так, как числа с плавающей запятой или целые числа, например div(//) и mod(%).
Для десятичных чисел оператор // выполняет усеченное деление:
x // y = trunc( x / y)
Класс Decimal предоставляет некоторые математические операции, такие как sqrt и log. Однако он не имеет всех функций, определенных в математическом модуле.
Когда вы используете функции из модуля math для десятичных чисел, Python преобразует объекты Decimal в числа с плавающей запятой перед выполнением арифметических операций. Это приводит к потере точности десятичных объектов.