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:
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')
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.
from decimal import Decimal Decimal('0.05') + Decimal('0.1') # returns Decimal('0.15') Decimal(0.05) + Decimal(0.1) # 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.
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')
Também podemos usar algumas funções matemáticas como sqrt()
, exp()
e log()
com decimais. Eis alguns exemplos:
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')
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:
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)
Também realizamos operações matemáticas simples (adição e subtração) em frações, como em números normais.
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
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
.
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
Também podemos usar o módulo com várias funções de math para realizar cálculos de frações.
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)
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.