Модули Decimal и Fraction
Модули Decimal и Fraction

Модули Decimal и Fraction

Из-за ограничений в сохранении точного значения чисел, даже простейшие математические операции могут выдавать ошибочный результат. Эти ограничения легко преодолимы – достаточно использовать десятичный модуль Decimal в Python. В выполнении расчетов на основе дробей поможет модуль фракций – Fraction. О принципах работы Decimal и Fraction и пойдет речь в данном обзоре.

Для чего нужен модуль Decimal?

Некоторые пользователи задаются вопросом, зачем нам нужен модуль для выполнения простейшей арифметики с десятичными числами, когда мы вполне можем сделать то же самое с помощью чисел с плавающей точкой 🤷‍♂️?

Перед тем, как мы ответим на данный вопрос, мы хотим, чтобы вы сами посчитали в Python, какой результат будет в данном примере: 0.1+0.2? Вы будете удивлены, когда узнаете, что правильный ответ – это не 0,3, а 0,30000000000000004.

Чтобы понять, почему в расчетах возникла ошибка, попробуйте представить 1/3 в десятичной форме. Тогда вы заметите, что число на самом деле не заканчивается в базе 10. Так как все числа должны быть каким-то образом представлены, при их сохранении в консоли делается несколько приближений, что и приводит к ошибкам.

Cпециально для читателей-гуманитариев, у нас есть объяснение принципов работы модулей Питона: «Она на долю секунды отвела взгляд» и "Она отвела взгляд на короткое время" — чувствуете разницу?

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

Модуль Decimal

Синтаксис

С помощью Decimal вы можете создавать десятичные числа.

Decimal обеспечивает поддержку правильного округления десятичной арифметики с плавающей точкой.

>>> from decimal import Decimal >>> number1 = Decimal("0.1") >>> number2 = Decimal("0.7") >>> print(number1 + number2) 0.8

Decimal, в отличие от float, имеет ряд преимуществ:

  • работает так же, как школьная арифметика;
  • десятичные числа представлены точно (в отличие от float, где такие числа как 1.1 и 5.12 не имеют точного представления);
  • точность десятичного модуля Decimal можно изменять (с помощью getcontext().prec);

Контекст

Базовые параметры Decimal можно посмотреть в его контексте, выполнив функцию getcontext():

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

Точность

Контекстом в Deciaml можно управлять, устанавливая свои значения. Например, для того, чтобы управлять точностью Decimal, необходимо изменить параметр контекста prec (от англ. precision – точность):

>>> from decimal import Decimal, getcontext >>> getcontext().prec = 2 >>> print(Decimal('4.34') / 4) 1.1 >>> getcontext().prec = 3 >>> print(Decimal('4.34') / 4) 1.08

Округление

Округление осуществляется с помощью метода quantize(). В качестве первого аргумента – объект Decimal, указывающий на формат округления:

>>> from decimal import Decimal >>> getcontext().prec = 4 # установим точность округление >>> number = Decimal("2.1234123") >>> print(number.quantize(Decimal('1.000'))) 2.123 # округление до 3 чисел в дробной части >>> print(number.quantize(Decimal('1.00'))) 2.12 # округление до 2 чисел в дробной части >>> print(number.quantize(Decimal('1.0'))) 2.1 # округление до 1 числа в дробной части

💁‍♀️ Важно: если точность округления установлена в 2 , а формат округления Decimal('1.00'), возникнет ошибка:

>>> print(number.quantize(Decimal('1.000'))) Traceback (most recent call last): File "<pyshell#78>", line 1, in <module> print(number.quantize(Decimal('1.00'))) decimal.InvalidOperation: [<class 'decimal.InvalidOperation'>]

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

>> getcontext().prec = 4 >>> print(number.quantize(Decimal('1.000'))) 2.123

Помимо первого параметра, quantize() принимает в качестве второго параметра стратегию округления:

  • ROUND_CEILING – округление в направлении бесконечности (Infinity);
  • ROUND_FLOOR – округляет в направлении минус бесконечности (- Infinity);
  • ROUND_DOWN – округление в направлении нуля;
  • ROUND_HALF_EVEN – округление до ближайшего четного числа. Число 4.9 округлится не до 5, а до 4 (потому что 5 – не четное);
  • ROUND_HALF_DOWN – округление до ближайшего нуля;
  • ROUND_UP – округление от нуля;
  • ROUND_05UP – округление от нуля (если последняя цифра после округления до нуля была бы 0 или 5, в противном случае к нулю).
>>> from decimal import Decimal, ROUND_CEILING >>> number = Decimal("0.029") >>> print(number.quantize(Decimal("1.00"), ROUND_CEILING)) 0.03Помните, что как округление, так и точность вступают в игру только во время арифметических операций, а не при создании самих десятичных дробей

Полезные методы Decimal

Итак, вот некоторые полезные методы для работы с десятичными числами в Decimal:

  • sqrt() – вычисляет квадратный корень из десятичного числа;
  • exp() – возвращает e^x (показатель степени) десятичного числа;
  • ln() – используется для вычисления натурального логарифма десятичного числа;
  • log10() – используется для вычисления log (основание 10) десятичного числа;
  • as_tuple() – возвращает десятичное число, содержащее 3 аргумента, знак (0 для +, 1 для -), цифры и значение экспоненты;
  • fma(a, b) – "fma" означает сложить, умножить и добавить. Данный метод вычисляет (num * a) + b из чисел в аргументе. В этой функции округление num * a не выполняется;
  • copy_sign() – печатает первый аргумент, копируя знак из второго аргумента.
📜 Полный список методов Decimal описан в официальной документации

Модуль Fraction

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

>>> from fractions import Fraction as frac >>> print(Fraction(33.33)) 2345390243441541/70368744177664 >>> print(Fraction('33.33')) 3333/100

Модуль Fraction особенно полезен, потому что он автоматически уменьшает дробь. Выглядит это вот так:

>>> Fraction(153, 272) Fraction(9, 16)

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

>>> Fraction(1, 2) + Fraction(3, 4) Fraction(5, 4)

Теперь давайте попробуем возвести дробь в степень:

>>> Fraction(1, 8) ** Fraction(1, 2) 0.3535533905932738

Когда использовать Decimal и Fraction?

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

  • Обмен валют. Особенно если этот процесс подразумевает не просто конвертацию евро в рубли, тенге или иную валюту, а выполнение более сложных операций.
  • Масштабируемые расчеты. К примеру, на фабрике начинают готовить печенье по бабушкиному рецепту, в котором упоминается "1/3 столовой ложки" определенного ингредиента. Сколько именно литров или миллилитров составит эта треть, если применять ее не к одной порции печенья, а к промышленным масштабам? А сколько это составит в пересчете на "неродную" систему мер, то есть фунтов или унций?
  • Работа с иррациональными числами. Если вы планируете запускать спутник или возводить энергетическую станцию, точность расчетов необходимо задать еще до того, как вы приступите к самым первым вычислениям. И оповестить об этом всех, кто имеет хоть какое-то отношение к проекту.

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

Модуль Decimal незаменим, если нужно считать деньги: с его помощью вы сможете подсчитать точную сумму, вплоть до копеек.

Fraction считает просто и честно: любители онлайн-игр приспособили его для подсчетов в игровой математике.

😭
😕
😃
😍