Artigos de Tecnologia e Negócios > Programação

Trabalhando com precisão em números decimais no Python

Yan Orestes
Yan Orestes

É bem possível que você já tenha se deparado com pequenos erros de precisão com números float, isso aconteceu comigo

Imagem de destaque

quando trabalhei em uma aplicação em Python para controle de gastos e ganhos da empresa onde trabalho.

A princípio, é simples, só guardo os valores em variáveis para depois passá-los para um banco de dados próprio. No mês de julho, tivemos 5 vendas de R$ 99.91 e compramos 3 equipamentos de R$ 110.10:


ganhos_julho = 99.91 * 5
gastos_julho = 110.1 * 3

armazena_no_banco(ganhos_julho, gastos_julho)

A partir disso, os especialistas em finanças da empresa fazem análises para tentar melhorar nossos resultados.

Meu código é bem simples, mas tem uma lógica clara que deveria funcionar bem. Apesar disso, no final do mês a chefia acabou me dando uma bronca. Isso porque alguns cálculos não bateram com os resultados reais que tivemos. Fiquei confuso, e fui testar meu código para conferir se tudo está como deveria:


ganhos_julho = 99.91 * 5
print(ganhos_julho)

gastos_julho = 110.1 * 3
print(gastos_julho)

Olha os resultados que obtive:


499.54999999999995
330.29999999999995

Espera… o quê? Fazendo as contas manualmente, ou na calculadora, os resultados são outros: 499.55 e 330.3. Por que o Python me entregou esses números compridos e, principalmente, imprecisos?

O problema do float

A lógica das nossas contas está correta, o problema, como vimos, está nos próprios resultados que o Python nos dá. O Python não sabe fazer conta direito, é isso?

Bem, a resposta pode ser sim… e não! Em primeiro lugar, essa questão não é exclusiva do Python, mas sim da computação e de como ela lida com números de ponto flutuante (nosso querido float). Além disso, não é exatamente um problema; vamos entender!

No mais baixo nível, computadores funcionam com a diferença de dois estados elétricos - baixa e alta voltagem, ligado e desligado, verdadeiro e falso. Daí temos o binário, o famoso 0 e 1, e é por conta desse sistema que os computadores conseguem ser tão rápidos com algumas coisas.

Entretanto, utilizando o formato binário para os números de ponto flutuante, os computadores não conseguem representar com precisão exata algumas frações (como 0.98 e 0.1). Desse modo, esses números são automaticamente arredondados para o mais próximo que se encaixe na possibilidade do binário, o que resulta em um pequeno erro de precisão.

No geral, esse erro é muito pequeno para ser considerado relevante, mas há situações em que não podemos desconsiderá-lo, como agora!

No nosso caso, como estamos lidando com dinheiro, precisamos de uma precisão maior. Já vimos como arredondar e formatar valores monetários antes, mas mesmo essa forma pode resultar em alguns pequenos erros de arredondamento que queremos evitar. E agora?

Trabalhando com inteiros

Como estamos trabalhando com dinheiro, podemos rapidamente pensar numa alternativa que evitaria o problema dos números de ponto flutuante - trabalhar apenas com números inteiros. Mas como? Sabemos que 1 real equivale a 100 centavos, então podemos, simplesmente, trabalhar com essa subunidade e evitar números quebrados.

No nosso caso, tínhamos R$ 188.98 e R$ 13.10 que também podem ser representados, respectivamente, por 18898¢ e 1310¢ - dois números inteiros!

Por estarmos trabalhando com o Python, ainda evitamos (a princípio) o problema de overflow de número inteiro padrão, que é muito limitado em algumas linguagens (como em Java, que só chega a 2.147.483.647).

Como o banco de dados está guardando inteiros, agora, podemos simplesmente dividir por 100 no momento de imprimir os valores, o que resolveria os problemas. Olha:


ganhos_julho, gastos_julho = pega_valores_no_banco()

print(ganhos_julho / 100)
print(gastos_julho / 100)

E a resposta:


188.98
13.1

Legal! Com o código arrumado, fui compartilhar com as filiais internacionais de minha empresa. Entretanto, rapidamente recebi diversas reclamações mostrando erros nos cálculos. Mas por quê?

A primeira reclamação veio direto de nossa filial na Tunísia. O programa estava apresentando cálculos claramente errados para eles - o banco de dados armazenava 10000 milim e o valor impresso era de 100 dinares tunisianos, quando deveria ser 10. Isso é porque, na Tunísia (e em diversos outros países) a subunidade de moeda principal não vem do centésimo (1/100), mas do milésimo (1/1000) - 1 dinar tunisiano equivale a 1000 milim.

Desse jeito, temos um grande problema em potencial com a internacionalização do código. Na verdade, mesmo que trabalhemos com apenas um local, o perigo continua. No mundo financeiro, subunidades mudam com o tempo, devido à inflação e deflação.

Se armazenarmos números inteiros, teremos que migrar os valores armazenados toda vez que houver uma mudança, o que pode atrapalhar bastante a manutenção de todo o sistema.

O ideal ainda seria conseguir trabalhar com a unidade principal da moeda, com números quebrados, mas com exatidão. Será que tem como?

Trabalhando com precisão com o tipo Decimal

Por conta dessa necessidade recorrente de lidarmos com números não-inteiros exatos, a maioria das linguagens de programação nos disponibiliza tipos específicos para lidar com isso.

No caso do Java, por exemplo, temos o BigDecimal. No Python, temos todo o módulo decimal e, mais especificamente, o tipo Decimal. Importando-o, seu uso é direto:


from decimal import Decimal

ganhos_julho = Decimal('99.91') * 5
print(ganhos_julho)

gastos_julho = Decimal('110.1') * 3
print(gastos_julho)

Dessa vez, olha o resultado:


499.55
330.3

Exatamente como na calculadora! Os analistas da empresa não terão mais nenhum problema com os cálculos.

Usando a precisão exata quando precisamos

Começamos com um problema o tipo float, do Python, não conseguia nos devolver um resultado exato de um cálculo. Logo entendemos que o problema não estava no Python, em si, mas no float e em como o computador lida com ele.

Como estávamos lidando com dinheiro, a precisão nos cálculos era fundamental. Assim, precisávamos de alguma solução. Demos uma olhada em como podemos transformar tudo em números inteiros (por exemplo, transformando o valor monetário de Real para centavos), o que, algumas vezes, pode ser uma boa saída.

Tratar todos os números como inteiros, entretanto, tem suas consequências negativas, como um possível overflow e problemas de manutenção de código. Queríamos uma solução melhor e… conseguimos!

Aprendemos que a maioria das linguagens de programação tem algum tipo numérico exato para evitar esse problema. No caso do Python, esse tipo é o Decimal, com precisão arbitrária. Com ele, nossos cálculos ganharam a precisão necessária e acabamos com todo o problema inicial.

Uma intuição natural depois de se conhecer os tipos numéricos exatos nas linguagens de programação, como o Decimal, é querer usá-los para tudo. Apesar disso, é importante sempre analisarmos se vale a pena - tipos exatos demandam mais tempo de processamento.

Normalmente, um pequeno erro na 10ª casa decimal de um número é irrelevante, e a precisão exata desnecessária. Por isso, temos sempre que analisar o contexto de nosso próprio programa antes de aplicar uma decisão.

Já conhecia o tipo Decimal antes? E toda essa confusão com o float? Escreva um comentário com sua opinião sobre o post e, se se interessar mais por Python, não deixe de dar uma olhada em nossa formação Python para web!

Leia também:

Artigos de Tecnologia e Negócios > Programação

Cursos profissionais de Programação é na Alura, comece agora!

  • 1017 cursos

    Cursos de programação, UX, agilidade, data science, transformação digital, mobile, front-end, marketing e infra.

  • Certificado de participação

    Certificado de que assistiu o curso e finalizou as atividades

  • App para Android e iPhone/iPad

    Estude até mesmo offline através das nossas apps Android e iOS em smartphones e tablets

  • Projeto avaliado pelos instrutores

    Projeto práticos para entrega e avaliação dos professores da Alura com certificado de aprovação diferenciado

  • Acesso à Alura Start

    Cursos de introdução a tecnologia através de games, apps e ciência

  • Acesso à Alura Língua

    Reforço online de inglês e espanhol para aprimorar seu conhecimento

Premium

  • 1017 cursos

    Cursos de programação, UX, agilidade, data science, transformação digital, mobile, front-end, marketing e infra.

  • Certificado de participação

    Certificado de que assistiu o curso e finalizou as atividades

  • App para Android e iPhone/iPad

    Estude até mesmo offline através das nossas apps Android e iOS em smartphones e tablets

  • Projeto avaliado pelos instrutores

    Projeto práticos para entrega e avaliação dos professores da Alura com certificado de aprovação diferenciado

  • Acesso à Alura Start

    Cursos de introdução a tecnologia através de games, apps e ciência

  • Acesso à Alura Língua

    Reforço online de inglês e espanhol para aprimorar seu conhecimento

12X
R$75
à vista R$900
Matricule-se

Premium Plus

  • 1017 cursos

    Cursos de programação, UX, agilidade, data science, transformação digital, mobile, front-end, marketing e infra.

  • Certificado de participação

    Certificado de que assistiu o curso e finalizou as atividades

  • App para Android e iPhone/iPad

    Estude até mesmo offline através das nossas apps Android e iOS em smartphones e tablets

  • Projeto avaliado pelos instrutores

    Projeto práticos para entrega e avaliação dos professores da Alura com certificado de aprovação diferenciado

  • Acesso à Alura Start

    Cursos de introdução a tecnologia através de games, apps e ciência

  • Acesso à Alura Língua

    Reforço online de inglês e espanhol para aprimorar seu conhecimento

12X
R$100
à vista R$1.200
Matricule-se

Max

  • 1017 cursos

    Cursos de programação, UX, agilidade, data science, transformação digital, mobile, front-end, marketing e infra.

  • Certificado de participação

    Certificado de que assistiu o curso e finalizou as atividades

  • App para Android e iPhone/iPad

    Estude até mesmo offline através das nossas apps Android e iOS em smartphones e tablets

  • Projeto avaliado pelos instrutores

    Projeto práticos para entrega e avaliação dos professores da Alura com certificado de aprovação diferenciado

  • Acesso à Alura Start

    Cursos de introdução a tecnologia através de games, apps e ciência

  • Acesso à Alura Língua

    Reforço online de inglês e espanhol para aprimorar seu conhecimento

12X
R$120
à vista R$1.440
Matricule-se
Procurando planos para empresas?
Acesso por 1 ano
Estude 24h/dia onde e quando quiser
Novos cursos toda semana