Diferença entre Float e Decimal no Python 3

Quando vamos trabalhar com números decimais em Python 3 temos sempre que cuidar com o quesito precisão. Se precisão extrema de cálculo não importar para o problema que intentamos resolver, Float vai nos servir muito bem.

Essa imprecisão, embora seja estranha a primeira vista, é completamente esperada e documentada, conforme as normas IEEE 754 e IEEE 854, como podemos ver na documentação do Oracle e esse comportamento não é uma exclusividade do Python, mas acontece em outras linguagens de programação, tais como C/C++, Java dentre outras. Existe o site The Floating-Point-gui que traz muita informação a esse respeito, pois não é o caso agora de aprofundarmos nisso.

Contudo, quando a aplicação mexe com dinheiro isso é bem mais sério. Isso porque ao somarmos R$1.10 com R$1.01, o float não nos voltará o esperado R$2.11, como podemos ver no trecho de código abaixo:

var1 = 1.10
var2 = 1.01
print(var1 + var2)
2.1100000000000003

Apesar do resultado ter colocado uma porção grande de zeros seguidas de um três, o que num arredondamento não causaria substancial diferença, como vemos abaixo:

var1 = 1.10
var2 = 1.01
print(f'O valor arredondado é: {var1 + var2:.2f}')
O valor arredondado é: 2.11

Não podemos contar que essa será sempre a realidade em tempo de execução de nossa aplicação.

Vejamos outro exemplo, que mostrará que a coisa é bem mais séria do que imaginamos quando se mexe com dinheiro:

# Saída com float
print(4.35 * 100)
434.99999999999994
# Saída com Decimal
from decimal import Decimal
print(Decimal('4.35') * 100)
435.00

Veja, ao se multiplicar 4.35 por 100 com o tipo Float temos 434.99999999999994 como resultado, e não o esperado 435. Já com o uso do tipo Decimal tudo fica como o esperado. Esse é um dos motivos para termos a lib Decimal no Python.

Vejamos o primeiro exemplo, o das var1 e var2, agora usando o formato Decimal em vez do formato Float:

from decimal import Decimal
var1 = Decimal('1.10')
var2 = Decimal('1.01')
print(var1 + var2)
2.11

Como pudemos ver, com a utilização da lib Decimal, não precisamos depender da formatação de saída da f-string e o próprio Decimal cuidou para que tivessemos o valor ajustado.

Esse segundo exemplo deixa claro que o Float tem suas discrepancias ao se modelar decimais quando vamos trabalhar com dinheiro.

Agora, para fechar com chave de ouro esse pequeno artigo, vamos combinar o uso do Decimal com o Local para apresentar o valor como moeda corrente, no caso nosso bom e velho Real, i. e., R$ 1.254.897,25 😉

from decimal import Decimal
import locale

locale.setlocale(locale.LC_ALL, 'pt_BR') 
# Aqui definimos que nosso local é Brasil 
# e isso implica a utilização do R$ para o currency
# além do uso do ponto para separar as casas de centenas, milhares etc
# e a vírgula para separar os centavos

valor = Decimal('1254897.25')
print(locale.currency(valor, grouping=True))
R$ 1.254.897,25

Aqui, o pulo do gato está em usar o argumento grouping=True no métdo curryency do objeto locale, pois com ele conseguimos que a formatação do número siga o padrão para moedas no Brasil, ou seja, ponto separando as casas das centenas, dos milhares etc e a vírgula para separar os centavos.

Vale lembrar que a saída de local.currency(valor, grouping=True) é uma string. Assim, qualquer cálculo deve ser realizado antes do uso desse método, i. e., seu uso deve ser exclusivamente para a saída formatada do valor em Reais.

Uma observação que devo manter aqui é que tem que se ter em conta que isso fucionará 100% em servidores localizados no Brasil ou configurados com o Locale pt-BR. Caso trabalhe com uma aplicação que necessita de internacionalização deve-se ter cuidado e verificar todos os cenários possíveis para se ter o que realmente se espera. Para isso nada melhor do que cobrir a aplicação con testes, ou melhor, construir a aplicação com TDD – Test Driven Development.

Espero que esse pequeno artigo o ajude no trato dos valores em Reais (ou outras moedas) que sua aplicação faz.

Ah! Não posso encerrar esse artigo sem deixar meus agradecimentos ao Renzo Nuccitelli, David Kwast e Fabio C. Barrionuevo da Luz pela troca de ideias no Telegram que deram origem a essa artigo. Muito obrigado meus camaradas!

Até a próxima!

About Prof. Vicente E. R. Marçal

Professor Adjunto do Departamento de Filosofia da Universidade Federal de Rondônia. Doutorando em Psicologia Social pelo Instituto de Psicologia da USP. Mestre em Filosofia pela Universidade Estadual Paulista/Campus Marília. Especialista em Filosofia Moderna e Contemporânea: Aspectos Éticos e Jurídicos pela Universidade Estadual de Londrina. Licenciado em Filosofia pela Universidade Estadual de Londrina. Coordenador do GEPEGRA - Grupo de Estudos e Pesquisa em Epistemologia Genética da Região Amazônica. Com experiência em Filosofia, com ênfase em Epistemologia e Teoria do Conhecimento.

2 thoughts on “Diferença entre Float e Decimal no Python 3

Deixe uma resposta

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *