Dinheiro em código: formatar em reais (R$) sem erro de centavos
Valores em dinheiro não devem ser guardados nem somados como número com vírgula flutuante: 0,1 + 0,2 dá 0,30000000000000004. Este guia mostra como guardar dinheiro em centavos, formatar em reais (R$ 1.234,50) em JavaScript, Python e PHP, ler o valor digitado pela pessoa e dividir uma compra em parcelas sem sobrar nem faltar centavo.
Por que guardar em centavos
Números como 0.1 não têm representação exata em binário, então pequenos erros se acumulam: 0.1 + 0.2 dá 0.30000000000000004, e (1.005).toFixed(2) dá "1.00", não "1.01". Em valores de dinheiro, esse erro vira um centavo a mais ou a menos na fatura.
- Guarde e some valores em centavos, como números inteiros: R$ 12,34 vira
1234. - No banco, use
DECIMAL(15,2)(ouNUMERIC) ou um inteiro de centavos. NuncaFLOATouDOUBLE. - No Python, use
Decimalpara contas com casas decimais. No JavaScript, use centavos inteiros: até 9 quatrilhões de centavos, a conta é exata.
Em JavaScript
O Intl.NumberFormat com currency: "BRL" já faz o formato brasileiro, com ponto nos milhares e vírgula nos centavos. Atenção em testes: entre o R$ e o número ele põe um espaço não separável (o caractere \u00A0), então "R$ 1.234,50" digitado com espaço comum não é igual ao resultado.
// Guarde e some valores em centavos (inteiros). Em reais com ponto flutuante,
// 0.1 + 0.2 dá 0.30000000000000004.
const reais = new Intl.NumberFormat('pt-BR', { style: 'currency', currency: 'BRL' });
function formatarReais(centavos) {
return reais.format(centavos / 100); // "R$ 1.234,50", com espaço não separável
}
// "1.234,56" ou "R$ 1.234,56" para 123456 centavos (ignora a partir da 3ª casa)
function paraCentavos(texto) {
const limpo = texto.replace(/[^\d,-]/g, '');
const [inteiro, fracao = ''] = limpo.split(',');
const sinal = inteiro.startsWith('-') ? -1 : 1;
const centavos = Number(fracao.padEnd(2, '0').slice(0, 2));
return sinal * (Math.abs(Number(inteiro || 0)) * 100 + centavos);
}
// Divide sem perder centavo: a sobra vai para as primeiras parcelas
function dividirEmParcelas(totalCentavos, parcelas) {
const base = Math.floor(totalCentavos / parcelas);
const sobra = totalCentavos - base * parcelas;
return Array.from({ length: parcelas }, (_, i) => base + (i < sobra ? 1 : 0));
} paraCentavos lê o valor como a pessoa digita: com ou sem R$, com ponto de milhar e vírgula.
Dividir em parcelas sem perder centavo
R$ 100,00 em 3 parcelas não dá três parcelas iguais: 3 × R$ 33,33 = R$ 99,99. Arredondar cada parcela some com um centavo, ou cria um a mais. O certo é dividir em centavos, pegar a sobra e somar 1 centavo nas primeiras parcelas: R$ 33,34 + R$ 33,33 + R$ 33,33. A função dividirEmParcelas faz isso, e o total sempre bate.
Em Python e PHP
No Python, o locale depende do sistema operacional e pode nem ter o português instalado no servidor, então é mais seguro formatar à mão. O round(2.675, 2) dá 2.67, porque 2.675 em float é um pouco menor que isso; com Decimal e ROUND_HALF_UP, dá 2.68. No PHP, o number_format resolve sem extensão; o NumberFormatter exige a extensão intl.
from decimal import ROUND_HALF_UP, Decimal
def formatar_reais(valor: Decimal) -> str:
# Decimal e não float: round(2.675, 2) dá 2.67, porque 2.675 não existe em float
valor = valor.quantize(Decimal("0.01"), rounding=ROUND_HALF_UP)
texto = f"{abs(valor):,.2f}".replace(",", "_").replace(".", ",").replace("_", ".")
return ("-R$ " if valor < 0 else "R$ ") + texto
def dividir_em_parcelas(total_centavos: int, parcelas: int) -> list[int]:
base, sobra = divmod(total_centavos, parcelas)
return [base + (1 if i < sobra else 0) for i in range(parcelas)] <?php
function formatarReais(int $centavos): string
{
$sinal = $centavos < 0 ? '-' : '';
return $sinal . 'R$ ' . number_format(abs($centavos) / 100, 2, ',', '.');
}
function dividirEmParcelas(int $totalCentavos, int $parcelas): array
{
$base = intdiv($totalCentavos, $parcelas);
$sobra = $totalCentavos - $base * $parcelas;
return array_map(fn($i) => $base + ($i < $sobra ? 1 : 0), range(0, $parcelas - 1));
} Recebe centavos inteiros, como o banco deve guardar.
Cuidados
- Arredonde uma vez, no fim da conta, e não a cada passo. Juros e descontos calculados com arredondamento em cada parcela acumulam diferenças.
- Combine a regra de arredondamento com o financeiro: "metade para cima" é a mais comum, mas há sistemas que usam o arredondamento bancário (metade para o par).
- No CSV para o Excel em português, use vírgula decimal. Veja o guia de CSV no Excel.
- Em JSON, valores em centavos inteiros evitam que cada linguagem leia o decimal de um jeito. Veja como formatar e validar JSON.
Achou um erro neste guia? Avise por e-mail.
Perguntas frequentes
Como formatar um valor em reais em JavaScript?
Use new Intl.NumberFormat("pt-BR", { style: "currency", currency: "BRL" }).format(1234.5), que devolve R$ 1.234,50. Entre o R$ e o número fica um espaço não separável, o caractere U+00A0.
Por que não usar float para dinheiro?
Porque frações como 0,1 não têm representação exata em binário, e os erros se acumulam: 0,1 + 0,2 dá 0,30000000000000004. Guarde centavos em inteiros ou use um tipo decimal.
Como dividir um valor em parcelas sem perder centavos?
Divida o total em centavos pelo número de parcelas, pegue a sobra da divisão e acrescente 1 centavo nas primeiras parcelas até a sobra acabar. Assim a soma das parcelas é sempre igual ao total.
Qual tipo usar no banco de dados para dinheiro?
DECIMAL ou NUMERIC com duas casas, como DECIMAL(15,2), ou um inteiro com o valor em centavos. FLOAT e DOUBLE guardam o valor com erro de arredondamento e não devem ser usados.
Outros guias
- Como formatar e validar JSON em JavaScript, Python e PHPComo deixar JSON legível e descobrir o erro de um JSON inválido em JavaScript, Python e PHP, com os erros mais comuns e as diferenças entre as linguagens.
- CSV no Excel em português: ponto e vírgula, acentos e zeros à esquerdaPor que o CSV abre numa coluna só ou com acentos quebrados no Excel em português e como gerar e ler CSV certo em JavaScript, Python e PHP, com código testado.