Executando verificação de segurança...
9

Qual tipo de dados devemos usar para valores monetários

Entendendo o problema

Estive lendo artigos e pensei em passar essa informação por aqui, esse post está aberto a incrementações do assunto e correção de erros.

No geral, antes de mais nada, qual tipo você usaria? O mais lógico a se pensar, seria o float, correto? Mas infelizmente, ele talvez seja a pior das opções, vamos falar um pouco do porque disso.

O float da maioria das linguagens é baseado no IEEE-754, onde há o cálculo do expoente e de sua mantissa, veja essa imagem de exemplo:
Exemplo float

Nesse modelo, quanto mais próximo o valor estiver do 0, mais impreciso ele será, entendeu agora o famoso problema do 0.1 + 0.2 = 0.3000000...4? Então, vamos lembrar que estariamos mexendo com dinheiro, talvez você não se importasse de perder alguns centavos, mas para um banco ou corretora, isso é uma perda gigantesca, tendo em vista a quantidade de dinheiro movimentado.

Então devemos usar o que?

A maioria das linguagens não possuem suporte ao tipo decimal como a maioria dos banco de dados, onde costumamos por decimal(15,2). No geral, esse tipo é mais recomendado por conta de sua precisão, tendo menos chances de falhas do que Float e Double.

Mas, caso sua linguagem não possua Decimal e você não pretenda usar nenhuma biblioteca para resolver esse problema, então, lhe convido a usar integer.

Inteiro? Como assim, e os centavos?

Toda linguagem suporta o tipo int (tirando o JS que usa Number, mas tem como tratar).
Na verdade você irá contar o valor monetário a partir dos centavos, dentro do inteiro, ou seja, 1,50 seria 150. A ideia é que o backend não precisa se importar com o que é mostrado para o usuário, então salvar o dado como inteiro gera menos risco de perda de valores.

Quando for necessário exibir o valor para o usuário, basta dividir o valor por 100, assim, criando novamente as 2 casas decimais para os centavos. Sem gerar o problema citado anteriormente.

30 / 100 = 0.3 e não 0.3000...4

Conclusão

No geral, são casos e casos, há pessoas que usam String e convertem para inteiro quando desejam realizar algum cálculo, isso entra no assunto "devemos usar string em números, quando não calculamos os mesmos, como CPF", mas isso é outro tópico 😉.

3

Recentemente tive problema com um app flutter el produção. Lá nesse app eu gero um boleto bancário cujo valor vem da multiplicação entre 3 números com 3 casas decimais. A probabilidade desse resultado ser um número inteiro é baixíssima. Nenhum dos meus testes previu essa possibilidade. Resultado: Um dos boletos falhava ao tentar ser aberto no app antes de imprimir na impressora bluetooth. O boleto abria no sistema web mas nao no app. Todos os outros boletos abriam no app, exceto um.

🤣🤣🤣

Deu um trabalho chato de achar o erro mas era exatamente isso. O tipo Double do Dart não aceita receber um número sem casas decimais. Nunca imaginei que esse erro pudesse acontecer pois todas as linguagens que eu já trabalhei aceitam. Um inteiro não aceita um double mas um double deveria aceitar um inteiro. Fica a dica pra quem estiver desenvolvendo app em flutter.

3

no VisualBASIC eu uso ccur; que é destinado justamente para valores monetários.
Mas no baixo nível o ccur é armazenado na memória exatamente como um inteiro long long e dividido por 100 ao ler a variável.

2

Em java sempre trabalhei com BigDecimal para valores monetários. Float também não é adequado.
E há casos que presenciei em sistemas ERP que trabalham com valores monetários com mais de duas casas decimais. Neste caso, usar um inteiro seria um problema.

1

Por que inteiro seria um problema? Teria como detalhar? Contando que saibamos onde começa as casas decimais, creio que basta apenas adicionar um 0 no cálculo. 3 casas = /1000 | 4 casas = /10000 e assim vai.

4

Nao vou responder pelo mauriciobinda, mas eu vou expor a minha experiencia.

Eu trabalhei em um sistema financeiro que tambem tinha uma biblioteca privada para operacoes monetarias. A gente usave o BigDecimal para representar valores mas todas as operacoes (divisao, juros, etc) eram realizadas em uma biblioteca proprietaria.

Eu lembro que o caso simples era: Se tenho 10 e parcelo em 3 vezes quais sao os valores das parcelas? Com certeza nao pode ser `3.33333333` de uma divisao simples. Neste exemplo a biblioteca responderia algo do tipo `parcela_1=3.33`, `parcela_2=3.33`, `parcela_3=3.34` cuja soma 10.

Este era o caso mais simples, aumenta a dificuldade quando incluimos juros ou muitas entidades (client, banco, etc) no problema.

Mas este eh um caso mais especifico da nossa aplicacao. Faz muito tempo que trabalhei com isso, mas lembro que existem alguns padroes defenidos dependendo do tipo da entidade (banco vs corretora) etc.

1

Quando não houver BigDecimal o melhor que considero é o long (inteiro de maior precisão). No caso do long mais trabalho encapsular as regras de manipulação de valores, mas evita uma dor de cabeça gigante que os tipos float e double podem causar.

1
-2

Obrigado pelo artigo! É sempre importante lembrar que precisão é crucial quando se trata de valores monetários. O uso do tipo decimal é a melhor opção em linguagens que o suportem, mas caso isso não seja possível, o uso do inteiro como alternativa é uma boa opção. É importante evitar o uso do tipo float para esse fim, pois ele pode causar imprecisão nos cálculos.

Boa postagem! Estou ansioso para ler mais sobre esse e outros tópicos relacionados.

Este comentário foi gerado por uma inteligência artificial. Para saber mais, leia esta publicação.

1

Isto me fez lembrar em uma integração entre sistemas, que o campo monetário no WebService era string. No primeiro momento achamos estranhos, depois acreditavamos ser um erro de projeto e até virou piada.

Mas, a piada virou para nós. Como o valor monetário era muito grande na casa antes da virgula, gerava esta incosistência do post, gerando valores errados.

Lição sobre isto, pesquise antes de tirar suas conclusões de uma solução, ela pode estar assim por algum motivo que você desconheça.