Использование модуля decimal в Python

Использование модуля decimal в Python

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

Введение

В Python есть числовые типы данных, такие как int, float и комплексные числа, но из-за машинной зависимости чисел с плавающей точкой нам нужен более точный тип данных для вычислений, требующих высокой точности. В этой статье мы рассмотрим модуль decimal в Python, который реализует десятичные числа с точностью до 28 цифр.

Использование

Python реализует десятичные числа как числа двойной точности с плавающей точкой, которые зависят от машины. Для вычислений, где точность критична для бизнеса, числа с плавающей точкой могут вызывать ошибки при выполнении на другой машине. Поэтому для таких приложений нам нужен независимый от машины тип данных для реализации десятичных чисел, который был реализован с помощью модуля decimal в Python. Кроме того, модуль decimal реализует десятичное число с точностью до 28 десятичных цифр, в то время как числа с плавающей запятой имеют точность до 18 цифр.

Это можно наблюдать в следующем примере

import decimal

float_num = 100 / 3
print(float_num)
decimal_num = decimal.Decimal(100) / decimal.Decimal(3)
print(decimal_num)

Вывод программы

33.333333333333336
33.33333333333333333333333333

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

Из-за аппроксимации значения с плавающей точкой дают разные результаты для разных вычислений. Например, если мы сложим 1,2 и 2,2, используя значения с плавающей точкой, ответ должен быть равен 3,4. Но когда мы сравним добавленное число и 3,4, они не будут равны. Эта ошибка возникает из-за приближений в числах с плавающей запятой, из-за которых сумма 1,2 и 2,2 не равна 3,4.

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

Это может быть более понятно из следующего примера.

a = 1.2
b = 2.2
c = 3.4
d = a + b
print("a:", a)
print("b:", b)
print("c:", c)
print("a+b:", d)
print("a+b==c?:", d == c)

Вывод программы

a: 1.2
b: 2.2
c: 3.4
a+b: 3.4000000000000004
a+b==c?: False

Настройка контекста

Чтобы использовать модуль decimal в Python, достаточно его импортировать следующим образом.

import decimal

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

import decimal

print(decimal.getcontext())

Вывод программы

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

Мы также можем установить точность и другие параметры контекста. Чтобы изменить точность на 2 цифры вместо 28, мы можем использовать следующую программу.

import decimal

decimal.getcontext().prec = 2
print(decimal.getcontext())

Вывод программы

Context(prec=2, rounding=ROUND_HALF_EVEN, Emin=-999999, Emax=999999, capitals=1, clamp=0, flags=[], traps=[InvalidOperation, DivisionByZero, Overflow])

По умолчанию, при округлении десятичных чисел с помощью модуля decimal, числа округляются равномерно. Мы можем изменить это поведение, изменив значение «rounding» в контексте следующим образом.

import decimal

decimal.getcontext().rounding = "ROUND_HALF_DOWN"
print(decimal.getcontext())

Вывод программы

Context(prec=28, rounding=ROUND_HALF_DOWN, Emin=-999999, Emax=999999, capitals=1, clamp=0, flags=[], traps=[InvalidOperation, DivisionByZero, Overflow])

Все арифметические операции над десятичными числами, определяемые модулем decimal, аналогичны операциям над числами с плавающей запятой. Разница заключается в точности значений из-за различий в реализации.

Округление значений

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

import decimal

num1 = decimal.Decimal(100)
num2 = decimal.Decimal(3)
print("Первое число:", num1)
print("Второе число:", num2)
num3 = num1 / num2
print("Первое число разделенное на второе число:", num3)
num4 = round(num3, 2)
print("Округленное число:", num4)

Вывод программы

Первое число: 100
Второе число: 3
Первое число разделенное на второе число: 33.33333333333333333333333333
Округленное число: 33.33

Сравнение десятичных чисел

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

import decimal

a = decimal.Decimal("1.2")
b = decimal.Decimal("2.2")
c = decimal.Decimal("3.4")
d = a + b
print("a:", a)
print("b:", b)
print("c:", c)
print("a+b:", d)
print("a+b==c?:", c == d)

Вывод программы

a: 1.2
b: 2.2
c: 3.4
a+b: 3.4
a+b==c?: True

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

Заключение

В этой статье мы изучили недостатки выполнения арифметических операций с числами с плавающей точкой и использовали модуль decimal для реализации тех же операций без ошибок в Python. Мы также можем написать программы, использованные в этой статье, с обработкой исключений, используя python try except, чтобы сделать программы более надежными и систематически обрабатывать ошибки.

Оставайтесь с нами для получения новых информативных статей.

Егор Егоров

Программирую на Python с 2017 года. Люблю создавать контент, который помогает людям понять сложные вещи. Не представляю жизнь без непрерывного цикла обучения, спорта и чувства юмора.

Ссылка на мой github есть в шапке. Залетай.

Оцените автора
Егоров Егор
Добавить комментарий

  1. Александр

    Егор, привет! Спасибо за сайт, много полезного!
    У меня есть вопрос. Очень важный для меня и моих расчетов.

    В python в функции для округлений round в случае последнего необходимого разряда числа после запятой = 5 действует так называемое банковское округления, кажется в сторону ближайшего нечетного разряда.
    Т.е. если число = 8,3235 надо округлить до 3 знаков после запятой, то round округлит его так 8,323.
    Но если требуется классическое математическое округление, которое при 5 — ке всегда округляет в большую сторону, надо танцевать с бубном.

    Подскажи пожалуйста наилучшее решение. Предыдущие поиски ничего не дали, к сожалению.

    Ответить
    1. Егор Егоров автор

      так есть же у модуля decimal режимы округления.

      https://docs.python.org/3/library/decimal.html#rounding-modes смотрели здесь? 🙂

      Ответить
  2. Андрей

    Всё доходчиво понятно….с примерами с докозательствами …циры наглядно подтверждают эфективность данной программы !

    Ответить
    1. Егор Егоров автор

      🙂

      Ответить
  3. Андрей

    Автор Молодец….статья интересная ….продолжай писать подобное )))

    Ответить
    1. Егор Егоров автор

      спасибо, Андрей

      Ответить