1. Code
  2. Python

Módulos Matemáticos em Python: Decimal e Frações

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.
Scroll to top
This post is part of a series called Mathematical Modules in Python.
Mathematical Modules in Python: Random
Mathematical Modules in Python: Statistics

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.