Даже самые простые математические операции могут иногда давать ошибочный результат. Это происходит из-за ограничений в сохранении точного значения некоторых чисел. Вы можете преодолеть эти ограничения, используя десятичный модуль в Python. Точно так же ни математика, ни модуль cmath, о которых мы узнали в нашем последнем уроке, не могут помочь нам в выполнении арифметики на основе дробей. Тем не менее, модуль фракций в Python делает именно это.
В этом руководстве вы узнаете как об этих модулях, так и о различных доступных им функциях.
Зачем нам десятичный модуль?
Вы, наверное, задаетесь вопросом, зачем нам нужен модуль для выполнения основной арифметики с десятичными числами, когда мы уже можем сделать то же самое с помощью чисел с плавающей точкой.
Прежде чем я отвечу на этот вопрос, я хочу, чтобы вы предположили выходное значение, если вы введете 0.1 + 0.2
в консоли Python. Если вы догадались, что выходное значение должно быть 0,3, вы будете удивлены, когда вы проверите фактический результат, который составляет 0,30000000000000004. Вы можете попробовать другой расчет, например, 0.05 + 0.1
и вы получите 0,15000000000000002.
Чтобы понять, что здесь происходит, попробуйте представить 1/3
в десятичной форме, и вы заметите, что число на самом деле не заканчивается в базе 10. Аналогично, некоторые числа, такие как 0,1 или 1/10, не заканчиваются в базе 2 Поскольку эти числа еще нужно как-то представить, при их сохранении делается несколько приближений, что приводит к этим ошибкам.
Число 0.30000000000000004 на самом деле очень близко к 0.3, поэтому большую часть времени мы можем избежать этого приближения. К сожалению, это приближение не приведет к сокращению, когда вы моделируете запуск спутника или имеете дело с деньгами. Другая проблема с этими приближениями состоит в том, что ошибки продолжают накапливаться.
Чтобы получить точные результаты, подобные тем, к которым мы привыкли при выполнении расчетов вручную, нам нужно что-то, что поддерживает быструю, правильно округленную, десятичную арифметику с плавающей запятой, и десятичный модуль делает именно это.
Использование десятичного модуля
Перед использованием модуля вам необходимо сначала его импортировать. После этого вы можете создавать десятичные числа из целых чисел, строк, чисел с плавающей точкой или кортежей. Когда десятичное число строится из целого числа или числа с плавающей запятой, происходит точное преобразование значения этого числа. Посмотрите на примеры ниже, чтобы понять, что я имею в виду:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
|
from decimal import Decimal
Decimal(121)
# returns Decimal(‘121’)
Decimal(0.05)
# returns Decimal(‘0.05000000000000000277555756’)
Decimal(‘0.05’)
# returns Decimal(‘0.05’)
Decimal((0, (8, 3, 2, 4), -3))
# returns Decimal(‘8.324’)
Decimal((1, (8, 3, 2, 4), -1))
# returns Decimal(‘-832.4’)
|
Как видите, значение Decimal(0.05)
немного отличается от Decimal('0.05')
. Это означает, что когда вы добавляете 0.05 и 0.1, вы должны использовать decimal.Decimal('0.05')
и decimal.Decimal('0.1')
для построения десятичных дробей.
1
2
3
4
5
6
7
|
from decimal import Decimal
Decimal(‘0.05’) + Decimal(‘0.1’)
# returns Decimal(‘0.15’)
Decimal(0.05) + Decimal(0.1)
# returns Decimal(‘0.1500000000000000083266726847’)
|
Теперь, когда вы можете выполнять различные операции с десятичными числами, вы можете захотеть контролировать точность или округление для этих операций. Это можно сделать с помощью функции getcontext()
. Эта функция позволяет вам, помимо прочего, получить и установить значение параметров точности и округления.
Помните, что как округление, так и точность вступают в игру только во время арифметических операций, а не при создании самих десятичных дробей.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
|
import decimal
from decimal import Decimal, getcontext
Decimal(1) / Decimal(13)
# returns Decimal(‘0.07692307692307692307692307692’)
getcontext().prec = 10
Decimal(0.03)
# returns Decimal(‘0.02999999999999999888977697537’)
Decimal(1) / Decimal(7)
# returns Decimal(‘0.1428571429’)
getcontext().rounding = decimal.ROUND_DOWN
Decimal(1) / Decimal(7)
# returns Decimal(‘0.1428571428’)
|
Вы также можете использовать некоторые математические функции, такие как sqrt()
, exp()
и log()
с десятичными знаками. Вот несколько примеров:
01
02
03
04
05
06
07
08
09
10
11
12
13
|
import decimal
from decimal import Decimal, getcontext
Decimal(2).sqrt()
# returns Decimal(‘1.414213562373095048801688724’)
getcontext().prec = 4
Decimal(‘2’).sqrt()
# returns Decimal(‘1.414’)
Decimal(‘2000’).log10()
# returns Decimal(‘3.301’)
|
Использование модуля фракций
Иногда вы можете столкнуться с ситуациями, когда вам нужно выполнить различные операции с дробями или конечный результат должен быть дробным. Модуль фракций может оказать большую помощь в этих случаях. Это позволяет вам создавать экземпляр Fraction
из чисел, чисел с плавающей запятой, десятичных дробей и даже строк. Как и в случае с десятичным модулем, у этого модуля есть несколько проблем, когда речь идет о создании дробей из чисел с плавающей точкой. Вот несколько примеров:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
|
from fractions import Fraction
from decimal import Decimal
Fraction(11, 35)
# returns Fraction(11, 35)
Fraction(10, 18)
# returns Fraction(5, 9)
Fraction(‘8/25’)
# returns Fraction(8, 25)
Fraction(1.13)
# returns Fraction(1272266894732165, 1125899906842624)
Fraction(‘1.13’)
# returns Fraction(113, 100)
Fraction(Decimal(‘1.13’))
# returns Fraction(113, 100)
|
Вы также можете выполнять простые математические операции, такие как сложение и вычитание, как обычные числа.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
|
from fractions import Fraction
Fraction(113, 100) + Fraction(25, 18)
# returns Fraction(2267, 900)
Fraction(18, 5) / Fraction(18, 10)
# returns Fraction(2, 1)
Fraction(18, 5) * Fraction(16, 19)
# returns Fraction(288, 95)
Fraction(18, 5) * Fraction(15, 36)
# returns Fraction(3, 2)
Fraction(12, 5) ** Fraction(12, 10)
# returns 2.8592589556010197
|
Модуль также имеет несколько важных методов, таких как limit_denominator(max_denominator)
который будет находить и возвращать дробь, наиболее близкую по значению к данной дроби, знаменатель которой не более max_denominator
. Вы также можете вернуть числитель заданной дроби в наименьшем члене, используя свойство numerator
а знаменатель — в свойстве denominator
.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
|
from fractions import Fraction
Fraction(‘3.14159265358979323846’)
# returns Fraction(157079632679489661923, 50000000000000000000)
Fraction(‘3.14159265358979323846’).limit_denominator(10000)
# returns Fraction(355, 113)
Fraction(‘3.14159265358979323846’).limit_denominator(100)
# returns Fraction(311, 99)
Fraction(‘3.14159265358979323846’).limit_denominator(10)
# returns Fraction(22, 7)
Fraction(125, 50).numerator
# returns 5
Fraction(125, 50).denominator
# returns 2
|
Вы также можете использовать этот модуль с различными функциями в математическом модуле для выполнения вычислений на основе дроби.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
|
import math
from fractions import Fraction
math.sqrt(Fraction(25, 4))
# returns 2.5
math.sqrt(Fraction(28,3))
# returns 3.0550504633038935
math.floor(Fraction(3558, 1213))
# returns 2
Fraction(math.sin(math.pi/3))
# returns Fraction(3900231685776981, 4503599627370496)
Fraction(math.sin(math.pi/3)).limit_denominator(10)
# returns Fraction(6, 7)
|
Последние мысли
Этих двух модулей должно быть достаточно, чтобы помочь вам выполнять общие операции как с десятичными, так и с дробными числами. Как показано в последнем разделе, вы можете использовать эти модули вместе с математическим модулем для вычисления значения всех видов математических функций в нужном вам формате.
В следующем уроке из этой серии вы узнаете о случайном модуле в Python.