Módulos Matemáticos em Python: Decimal e Frações
Portuguese (Português) translation by Erick Patrick (you can also view the original English article)
Até as operações matemáticas mais básicas podem, retornar valores errados. Isso é pelas limitações no armazenamento do valor de alguns números. Podemos passar pelas limitações usando o módulo decimal de Python. De forma similar, nem math ou cmath, que vimos no tutorial anterior, podem nos ajudar em aritimética de frações. Contudo, o módulo fractions do Python consegue.
Nesse tutorial, aprenderemos ambos os módulos e as diferentes funções disponíveis.
Por Que Precisamos de um Módulo Decimal?
Devemos nos perguntar o porque de precisar de um módulo para cálculos aritiméticos basico com números decimais quando já podemos usar pontos flutuantes.
Antes de responder a pergunta, gostaríamos que tentassem adivinhar o retorno de 0.1 + 0.2 se usando no console do Python. Se dissermos 0.3, tomaremos um susto ao verificar o resultado, que é 0.30000000000000004. Podemos tentar outros cálculos, como 0.05 + 0.1 que dá 0.15000000000000002.
Para entender o que acontece, tentemos representar 1/3 na forma decimal e percebamos que é uma dízima períodica na base 10. De forma similar, números como 0.1 (1/10) são dízimas periódicas na base 2. Como esse números ainda precisam de alguma representação, algumas aproximações são feitas durante seus armazenamentos, resultando em erros.
O número 0.30000000000000004 é bem próximo de 0.3, assim podemos esquecer a aproximação na maioria dos casos. Infelizmente, essa aproximação não pode passar quando simulamos o lançamento de um satélite ou lidando com dinheiro. Outro problema dessas aproximações é que os erros viram uma bola de neve.
Para obter resultados precisos como os que temos quando realizamos os cálculos à mão, precisamos de algo que suporte cálculo artitimético de pontos flutuantes rápidos e arredondados corretamente, e o módulo decimal faz isso.
Usando o Módulo Decimal
Antes de usá-lo, devemos importá-lo primeiro. Depois, podemos criar decimais a partir de inteiros, pontos flutuantes, cadeias de caracteres ou tuplas. Quando o decimal é criado de um inteiro ou ponto flutuante, a conversão é exata do valor para o número. Vejamos os exemplo abaixos para entender melhor:
1 |
from decimal import Decimal |
2 |
|
3 |
Decimal(121) |
4 |
# returns Decimal('121')
|
5 |
|
6 |
Decimal(0.05) |
7 |
# returns Decimal('0.05000000000000000277555756')
|
8 |
|
9 |
Decimal('0.05') |
10 |
# returns Decimal('0.05')
|
11 |
|
12 |
Decimal((0, (8, 3, 2, 4), -3)) |
13 |
# returns Decimal('8.324')
|
14 |
|
15 |
Decimal((1, (8, 3, 2, 4), -1)) |
16 |
# returns Decimal('-832.4')
|
Como vemos, o valor Decimal(0.05) é um pouco diferente de Decimal('0.05'). Isso significa que ao adicionar 0.05 e 0.1, devemos usar decimal.Decimal('0.05') e decimal.Decimal('0.1') para criar os decimais.
1 |
from decimal import Decimal |
2 |
|
3 |
Decimal('0.05') + Decimal('0.1') |
4 |
# returns Decimal('0.15')
|
5 |
|
6 |
Decimal(0.05) + Decimal(0.1) |
7 |
# returns Decimal('0.1500000000000000083266726847')
|
Agora que podemos realizar várias operações em decimais, queremos controlar a precisão ou arredondamento das operações. Isso pode ser feito usando a função getcontext(). Essa função permite-nos tanto obter quando configurar o valor da precisão e arredondamento, e outras coisas.
Tenhamos em mente que tanto arredondamento e precisão entram em jogo durante operações aritiméticas e não apenas quando criamos decimais em si.
1 |
import decimal |
2 |
from decimal import Decimal, getcontext |
3 |
|
4 |
Decimal(1) / Decimal(13) |
5 |
# returns Decimal('0.07692307692307692307692307692')
|
6 |
|
7 |
getcontext().prec = 10 |
8 |
|
9 |
Decimal(0.03) |
10 |
# returns Decimal('0.02999999999999999888977697537')
|
11 |
|
12 |
Decimal(1) / Decimal(7) |
13 |
# returns Decimal('0.1428571429')
|
14 |
|
15 |
getcontext().rounding = decimal.ROUND_DOWN |
16 |
|
17 |
Decimal(1) / Decimal(7) |
18 |
# returns Decimal('0.1428571428')
|
19 |
Também podemos usar algumas funções matemáticas como sqrt(), exp() e log() com decimais. Eis alguns exemplos:
1 |
import decimal |
2 |
from decimal import Decimal, getcontext |
3 |
|
4 |
Decimal(2).sqrt() |
5 |
# returns Decimal('1.414213562373095048801688724')
|
6 |
|
7 |
getcontext().prec = 4 |
8 |
|
9 |
Decimal('2').sqrt() |
10 |
# returns Decimal('1.414')
|
11 |
|
12 |
Decimal('2000').log10() |
13 |
# returns Decimal('3.301')
|
Usando o Módulo Fractions
Algumas vezes, lidamos com sitações onde precisamos realizar várias operações em franções ou o resultdo final precisa ser uma fração. O módulo fração pode ser de muita ajuda nessas horas. Ele permite criar uma instância de Fraction a partir de números, pontos flutuantes, decimais e até cadeias de caracteres. Como o módulo decimal, há alguns problemas ao criar frações a partir de pontos flutuantes. Eis alguns exemplos:
1 |
from fractions import Fraction |
2 |
from decimal import Decimal |
3 |
|
4 |
Fraction(11, 35) |
5 |
# returns Fraction(11, 35)
|
6 |
|
7 |
Fraction(10, 18) |
8 |
# returns Fraction(5, 9)
|
9 |
|
10 |
Fraction('8/25') |
11 |
# returns Fraction(8, 25)
|
12 |
|
13 |
Fraction(1.13) |
14 |
# returns Fraction(1272266894732165, 1125899906842624)
|
15 |
|
16 |
Fraction('1.13') |
17 |
# returns Fraction(113, 100)
|
18 |
|
19 |
Fraction(Decimal('1.13')) |
20 |
# returns Fraction(113, 100)
|
Também realizamos operações matemáticas simples (adição e subtração) em frações, como em números normais.
1 |
from fractions import Fraction |
2 |
|
3 |
Fraction(113, 100) + Fraction(25, 18) |
4 |
# returns Fraction(2267, 900)
|
5 |
|
6 |
Fraction(18, 5) / Fraction(18, 10) |
7 |
# returns Fraction(2, 1)
|
8 |
|
9 |
Fraction(18, 5) * Fraction(16, 19) |
10 |
# returns Fraction(288, 95)
|
11 |
|
12 |
Fraction(18, 5) * Fraction(15, 36) |
13 |
# returns Fraction(3, 2)
|
14 |
|
15 |
Fraction(12, 5) ** Fraction(12, 10) |
16 |
# returns 2.8592589556010197
|
O modulo também possui alguns métodos importantes como limit_denominator(max_denominator) que encontrará e retornará a fração mais próxima de uma fração cujo denominar é, no máximo, max_denominator. Também podemos retornar o numerador de uma fração no menor termo, usando a propriedade numerator, e o denominador usando a propriedade denominator.
1 |
from fractions import Fraction |
2 |
|
3 |
Fraction('3.14159265358979323846') |
4 |
# returns Fraction(157079632679489661923, 50000000000000000000)
|
5 |
|
6 |
Fraction('3.14159265358979323846').limit_denominator(10000) |
7 |
# returns Fraction(355, 113)
|
8 |
|
9 |
Fraction('3.14159265358979323846').limit_denominator(100) |
10 |
# returns Fraction(311, 99)
|
11 |
|
12 |
Fraction('3.14159265358979323846').limit_denominator(10) |
13 |
# returns Fraction(22, 7)
|
14 |
|
15 |
Fraction(125, 50).numerator |
16 |
# returns 5
|
17 |
|
18 |
Fraction(125, 50).denominator |
19 |
# returns 2
|
Também podemos usar o módulo com várias funções de math para realizar cálculos de frações.
1 |
import math |
2 |
from fractions import Fraction |
3 |
|
4 |
math.sqrt(Fraction(25, 4)) |
5 |
# returns 2.5
|
6 |
|
7 |
math.sqrt(Fraction(28,3)) |
8 |
# returns 3.0550504633038935
|
9 |
|
10 |
math.floor(Fraction(3558, 1213)) |
11 |
# returns 2
|
12 |
|
13 |
Fraction(math.sin(math.pi/3)) |
14 |
# returns Fraction(3900231685776981, 4503599627370496)
|
15 |
|
16 |
Fraction(math.sin(math.pi/3)).limit_denominator(10) |
17 |
# returns Fraction(6, 7)
|
Pensamentos Finais
Esse dois módulos devem ser suficientes para ajudar a realizar operações comuns tanto em decimais quanto frações. Como vimos na última seção, podemos usá-los junto do módulo math para calcular o valor de todo tipo de função matemática no formato desejado.
No próximo tutorial da série, aprenderemos sobre o módulo random em Python.



