# 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.

- Endereço oficial: https://pradev.com.br/guias/como-formatar-dinheiro-em-reais
- Tipo: Guia
- Publicado em: 2026-10-01
- Atualizado em: 2026-10-01
- Autor: Leonardo Gaertner (https://pradev.com.br/sobre)

## 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)` (ou `NUMERIC`) ou um inteiro de centavos. Nunca `FLOAT` ou `DOUBLE`.
- No Python, use `Decimal` para 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.

```javascript
// 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));
}
```

_JavaScript. 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.

```python
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)]
```

_Python_

```php
<?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));
}
```

_PHP. 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](https://pradev.com.br/guias/csv-excel-ponto-e-virgula-acentos).
- Em JSON, valores em centavos inteiros evitam que cada linguagem leia o decimal de um jeito. Veja [como formatar e validar JSON](https://pradev.com.br/guias/como-formatar-e-validar-json).

## 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.

## Fontes

- [MDN: Intl.NumberFormat](https://developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat)
- [Python: módulo decimal](https://docs.python.org/3/library/decimal.html)

## Guias relacionados

- [Como formatar e validar JSON em JavaScript, Python e PHP](https://pradev.com.br/guias/como-formatar-e-validar-json): Como 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 à esquerda](https://pradev.com.br/guias/csv-excel-ponto-e-virgula-acentos): Por 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.
