Статьи

Математические модули в Python: десятичные дроби и дроби

Даже самые простые математические операции могут иногда давать ошибочный результат. Это происходит из-за ограничений в сохранении точного значения некоторых чисел. Вы можете преодолеть эти ограничения, используя десятичный модуль в 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.