A matemática por trás dos cálculos financeiros que devs usam todo dia
Praticamente todo sistema acaba tendo que calcular algo com dinheiro: desconto, parcelamento, correção monetária, cashback, taxa de serviço. É código que parece trivial — "é só uma porcentagem" — até o dia em que o valor final não bate com o que a calculadora do financeiro deu, ou um teste automatizado começa a falhar de forma intermitente sem que ninguém tenha mexido na lógica. Na maioria das vezes, o problema não está no requisito de negócio, está numa das poucas armadilhas matemáticas que esse tipo de cálculo sempre traz: representação de número em ponto flutuante, ordem de operações em porcentagem composta, e a diferença entre juros simples e compostos aplicada errado.
Ponto flutuante não foi feito para representar dinheiro
O problema mais comum não é de fórmula, é de representação. Números como 0.1 e 0.2 não têm representação exata em binário — é a mesma razão pela qual 0.1 + 0.2 resulta em 0.30000000000000004 em praticamente qualquer linguagem que use float/double (IEEE 754). Em um cálculo isolado o erro é minúsculo e passa despercebido. Mas em uma rotina de parcelamento que soma centenas de valores, ou em um relatório que reconcilia milhares de lançamentos, esse erro de arredondamento se acumula até virar uma diferença de centavos visível — e diferença de centavos em sistema financeiro não é "aceitável", é motivo de ticket.
A prática padrão da indústria é nunca guardar dinheiro como número fracionário. Duas saídas resolvem isso:
- Inteiro em centavos: R$ 19,90 vira
1990(inteiro), e a formatação para exibição só acontece na camada de apresentação. Toda soma, subtração e multiplicação por quantidade fica livre de erro de ponto flutuante, porque inteiro tem representação binária exata. - Tipo decimal de precisão arbitrária:
BigDecimalem Java,decimal.Decimalem Python,DECIMAL/NUMERICno banco. Representa a base 10 de forma exata em vez de aproximar em base 2, ao custo de mais processamento por operação — custo irrelevante perto do risco de um valor financeiro errado.
O erro clássico é fazer o cálculo inteiro em float e só formatar como moeda no fim — nesse ponto, o arredondamento de exibição já escondeu o erro acumulado da conta, e ele só reaparece quando alguém soma os valores exibidos e o total não bate com o que o sistema calculou internamente.
Porcentagem parece óbvia até você compor duas
Calcular uma porcentagem isolada é trivial: valor × (percentual / 100). O erro aparece quando duas porcentagens se combinam e alguém assume que dá para simplesmente somá-las. Um desconto de 10% seguido de outro desconto de 10% não equivale a 20% de desconto — equivale a 19%, porque o segundo desconto incide sobre o valor já reduzido pelo primeiro: R$ 100 → R$ 90 (−10%) → R$ 81 (−10% sobre 90), e não R$ 80. A ordem de aplicação também importa quando há arredondamento intermediário: aplicar imposto antes ou depois de um desconto pode gerar centavos de diferença, e qual dos dois é o correto normalmente é uma regra de negócio (fiscal, contratual) — não uma escolha livre de quem implementa.
O mesmo raciocínio vale para aumento seguido de desconto do mesmo percentual: um produto que sobe 10% e depois cai 10% não volta ao preço original — fica 1% mais barato, porque os 10% de desconto incidem sobre uma base maior. É contraintuitivo o suficiente para gerar bug de negócio, não só bug de código, quando a regra é escrita em linguagem natural e implementada sem checar contra um caso numérico concreto.
Juros simples vs. juros compostos: a diferença está no expoente
As duas fórmulas se parecem o bastante no papel para alguém trocar uma pela outra sem perceber:
- Juros simples:
M = P × (1 + i × n)— o juro incide sempre sobre o capital inicial (P). Cresce de forma linear. - Juros compostos:
M = P × (1 + i)ⁿ— o juro de cada período incide sobre o saldo já acrescido de juros do período anterior (juro sobre juro). Cresce de forma exponencial.
Onde P é o capital inicial, i é a taxa de juros por período (em decimal) e n é o número de períodos. A diferença entre as duas fórmulas é pequena em prazos curtos e taxas baixas — o que faz o bug passar despercebido em teste manual — mas diverge rápido conforme n cresce, porque um é linear e o outro é exponencial. Financiamento, empréstimo e a maioria dos produtos bancários usam composto; cálculo de multa proporcional por dia de atraso costuma usar simples. Implementar a fórmula errada não gera erro de sintaxe nem exceção — gera um valor plausível e sutilmente errado, do tipo que só aparece quando alguém confere o resultado contra uma calculadora financeira de verdade ou contra a Tabela Price num contrato de longo prazo.
Outro detalhe que gera divergência de centavos: taxa anual convertida para taxa mensal. A conversão correta para juros compostos não é dividir a taxa anual por 12 (isso é conversão de juros simples aplicada onde não cabe) — é i_mensal = (1 + i_anual)^(1/12) − 1. Trocar uma fórmula pela outra é um erro comum o bastante para ter nome no mercado financeiro (taxa nominal vs. taxa efetiva), e a diferença entre as duas cresce junto com o valor principal e o prazo.
Regra de três: proporção direta não é sempre a resposta certa
A regra de três simples resolve proporção direta — se 5 itens custam R$ 50, 8 itens custam (8 × 50) / 5 = 80. O erro comum é aplicar essa mesma lógica em relações que são inversamente proporcionais, onde uma grandeza aumenta enquanto a outra diminui — por exemplo, quanto mais rápido algo é feito, menos tempo leva. Nesse caso o cálculo se inverte: em vez de multiplicar cruzado direto, multiplicam-se os pares originais e divide-se pelo novo valor. Uma calculadora de regra de três que sempre assume proporção direta vai devolver um número plausível e completamente errado para qualquer caso de proporção inversa, sem lançar erro nenhum — o resultado "parece" uma resposta válida porque é, matematicamente, só que para a pergunta errada.
Arredondamento: a regra que muda o centavo final
Depois que o cálculo está certo, ainda sobra a escolha de como arredondar o resultado para duas casas decimais — e diferentes estratégias dão respostas diferentes no limite:
- Arredondamento comum (metade para cima): 0,125 → 0,13. É o mais intuitivo, mas introduz um viés sistemático para cima quando aplicado em massa sobre muitos valores.
- Banker's rounding (metade para o par mais próximo): 0,125 → 0,12, mas 0,135 → 0,14. Usado por padrão em várias linguagens (o
round()do Python 3, por exemplo) justamente para eliminar o viés cumulativo em grandes volumes de operações — e é uma fonte clássica de "por que esse valor não bate com o Excel" quando duas ferramentas usam estratégias diferentes. - Truncamento: descarta o resto sem arredondar. Usado quando a regra de negócio (fiscal, contratual) exige explicitamente que a instituição nunca arredonde a favor do cliente.
Qual estratégia usar normalmente não é uma decisão técnica livre — é definida por norma contábil, contrato ou legislação tributária. O ponto de atenção para quem testa é outro: garantir que a mesma estratégia seja usada de forma consistente em todo o sistema. É comum um serviço usar uma estratégia e o serviço que reconcilia os valores usar outra, e a diferença só aparece quando os dois números são comparados lado a lado — exatamente o tipo de bug que teste unitário isolado por serviço não pega, porque cada um, isoladamente, está "certo".
Casos de teste que valem mais que os óbvios
- Soma repetida de um valor com dízima em ponto flutuante (ex.: somar 0,10 dez vezes) e comparar contra o valor esperado exato, não contra uma tolerância generosa demais que mascara o erro.
- Composição de dois percentuais na mesma direção (desconto sobre desconto) e em direções opostas (aumento seguido de desconto igual).
- Mesmo capital e taxa, comparando explicitamente o resultado de juros simples contra composto, para garantir que a fórmula certa está implementada onde deveria.
- Conversão de taxa anual para mensal usando a fórmula de juros compostos, não a divisão simples por 12.
- Valor exatamente no meio do arredondamento (ex.: ...,125) para confirmar qual estratégia está de fato implementada, não qual a documentação diz que deveria estar.
- Proporção inversa disfarçada de proporção direta, para garantir que a regra de três não está sendo aplicada cegamente.
As calculadoras de Percentagem, Regra de 3 e Juros Compostos deste site servem exatamente para esse tipo de conferência: gerar rapidamente o valor esperado de um cenário para comparar contra o que o seu sistema devolveu, sem precisar montar a fórmula na mão toda vez que escrever um caso de teste.