PHP Formatar Números
Para o PHP, mil duzentos e trinta e quatro reais e cinquenta centavos é 1234.5: ponto decimal, sem separador de milhar. Para um brasileiro, é R$ 1.234,50. Esta aula mostra como fazer essa "tradução" na hora de mostrar o número, e como arredondar do jeito certo.
number_formatcom 1, 2 e 4 argumentos, e o formato brasileiro1.234,56.- Mostrar dinheiro:
"R$ " . number_format(…). - Arredondar:
round,ceil,floor, e por que oprintfàs vezes arredonda diferente. - A regra de ouro: calcule com números, formate só no fim.
- Converter
"1.234,56"(digitado por um brasileiro) de volta para número. - O
NumberFormatterda extensão intl: moeda, por extenso, porcentagem, e quando ele está disponível.
A ideia: o número e a "roupa" do número
Pense no valor guardado no caixa da lojinha da Ana: é um número, e o PHP faz contas com ele. Quando o valor vai para a etiqueta de preço, ele ganha uma "roupa": o R$, a vírgula, o ponto do milhar. A roupa é só para ser lida por pessoas. Ninguém faz conta com a etiqueta.
Em PHP é igual: o número continua 1234.5 (um float; veja int e float), e number_format produz um texto vestido para o leitor.
number_format
<?php $valor = 1234.567; echo number_format($valor), "\n"; echo number_format($valor, 2), "\n"; echo number_format($valor, 2, ",", "."), "\n";
$ php formatar.php 1,235 « 1 argumento: sem casas, arredondado, milhar com vírgula (formato americano) 1,234.57 « 2 argumentos: 2 casas, ainda no formato americano 1.234,57 « 4 argumentos: o formato brasileiro
- number_format(
- "Formatar número". Devolve um texto.
- $valor,
- 1º: o número.
- 2,
- 2º: quantas casas decimais. O número é arredondado (1234.567 → 1234.57).
- ",",
- 3º: o separador decimal (no Brasil, a vírgula).
- "."
- 4º: o separador de milhar (no Brasil, o ponto).
Decore esta linha, você vai usá-la a vida toda: number_format($valor, 2, ",", ".").
Mais variações
<?php echo number_format(1234567.891, 2, ",", "."), "\n"; echo number_format(1234.567, 0, ",", "."), "\n"; echo number_format(1234.5, 2, ",", ""), "\n"; echo number_format(1234.5, 2, ",", " "), "\n"; echo number_format(-1234.5, 2, ",", "."), "\n"; echo number_format(1234.5, -2), "\n"; var_dump(number_format(1234.5, 2, ",", "."));
$ php variacoes.php 1.234.567,89 « milhões também 1.235 « 0 casas: inteiro, arredondado 1234,50 « separador de milhar vazio: sem ponto 1 234,50 « espaço como separador (usado em Portugal e na França) -1.234,50 1,200 « casas negativas arredondam à esquerda (centenas) — PHP 8.3+ string(8) "1.234,50" « o resultado é um TEXTO (string), não um número
Missão: o caixa da Ana tem $total = 1234.5. Mostre R$ 1.234,50, juntando o texto "R$ " com o number_format no formato brasileiro.
<?php $total = 1234.5; echo "R$ " . $total;
<?php $total = 1234.5; echo "R$ " . number_format($total, 2, ",", ".");
Dinheiro: "R$ " . number_format(…)
O number_format não coloca o símbolo da moeda: você concatena. A lojinha da Ana calcula o total de uma compra e mostra o recibo:
<?php $maca = 3 * 4.5; $uva = 2 * 7.9; $total = $maca + $uva; echo "Maçã: R$ " . number_format($maca, 2, ",", "."), "\n"; echo "Uva: R$ " . number_format($uva, 2, ",", "."), "\n"; echo "Total: R$ " . number_format($total, 2, ",", "."), "\n";
$ php recibo.php Maçã: R$ 13,50 Uva: R$ 15,80 Total: R$ 29,30
Repare na ordem: primeiro todas as contas com os números ($maca, $uva, $total), e só na linha do echo o número ganha a "roupa". Para não repetir number_format($x, 2, ",", ".") em todo lugar, você vai criar a sua própria função reais($x) na aula de Funções.
<?php $total = "R$ " . number_format(10, 2, ",", "."); echo $total + 5;
$ php conta-texto.php
Fatal error: Uncaught TypeError: Unsupported operand types: string + int in /home/ana/php/conta-texto.php:3
Stack trace:
#0 {main}
thrown in /home/ana/php/conta-texto.php on line 3
"Tipos de operando não suportados: string + int". "R$ 10,00" é um texto, não dá para somar. Mesmo sem o R$, "1.234,50" seria lido errado (veja abaixo). Guarde o número, formate só para mostrar.
Arredondar: round, ceil e floor
O number_format já arredonda, mas devolve texto. Quando você precisa de um número arredondado (para continuar fazendo contas), use estas funções:
<?php echo round(3.14159, 2), "\n"; echo round(2.5), " ", round(3.5), " ", round(-2.5), "\n"; echo round(1234.567, -2), "\n"; echo ceil(4.1), " ", floor(4.9), " ", intval(4.9), "\n"; var_dump(round(2.5));
$ php arredondar.php 3.14 « round com 2 casas 3 4 -3 « o meio (.5) vai para longe do zero, como na escola 1200 « casas negativas: arredonda para a centena 5 4 4 « ceil sobe, floor desce, intval corta float(3) « round devolve float, mesmo sem casas
| Função | Origem do nome | O que faz | 4.1 | 4.5 | 4.9 | -4.5 |
|---|---|---|---|---|---|---|
round | "arredondar" | Para o mais próximo; o meio vai para longe do zero | 4 | 5 | 5 | -5 |
ceil | ceiling, "teto" | Sempre para cima | 5 | 5 | 5 | -4 |
floor | "chão" | Sempre para baixo | 4 | 4 | 4 | -5 |
intval | integer value | Corta as casas (vira int) | 4 | 4 | 4 | -4 |
Um uso clássico do ceil: a Ana tem 47 alunos e cada van leva 15. Quantas vans? ceil(47 / 15) dá 4: não dá para alugar 3,13 vans, e 3 não bastam.
O round aceita um terceiro argumento com o "modo". round(2.5, 0, PHP_ROUND_HALF_EVEN) dá 2: o meio vai para o par mais próximo (o "arredondamento bancário"). No PHP 8.4+, o modo também pode ser escrito com o enum RoundingMode::HalfEven PHP 8.4+, que tem ainda RoundingMode::TowardsZero, AwayFromZero, PositiveInfinity… (enums são assunto de Enums).
Cuidado: o printf arredonda diferente
<?php
echo round(2.25, 1), "\n";
echo number_format(2.25, 1), "\n";
printf("%.1f\n", 2.25);
echo round(1.005, 2), " ", number_format(1.005, 2), " ", sprintf("%.2f", 1.005), "\n";
$ php diferenca.php 2.3 2.3 2.2 « o printf foi para baixo! 1.01 1.01 1.00 « de novo
Por quê? Números com casas decimais são guardados de forma aproximada (o famoso 0.1 + 0.2 que não dá exatamente 0.3; veja int e float). O número 1.005 na verdade está guardado como 1,00499999…, e o printf arredonda esse valor "verdadeiro". Já o round (e o number_format, que usa as mesmas regras) faz uma correção para dar o resultado que uma pessoa espera. Para dinheiro, prefira round e number_format.
Sistemas de pagamento costumam guardar valores em centavos, como inteiros: R$ 10,50 vira 1050. Inteiros não têm o problema de aproximação. Na hora de mostrar: number_format($centavos / 100, 2, ",", "."). Para cálculos com muitas casas, existe a extensão BCMath (bcadd, bcmul…), que faz contas com números em forma de texto, sem aproximação.
O caminho de volta: "1.234,56" para número
Quando um brasileiro digita um valor num formulário, ele vem como texto no formato brasileiro. Se você converter direto, o PHP lê só até onde entende:
<?php
var_dump((float) "1.234,56");
$brasileiro = "1.234,56";
$semPonto = str_replace(".", "", $brasileiro);
$comPonto = str_replace(",", ".", $semPonto);
$numero = (float) $comPonto;
var_dump($numero);
$ php voltar.php float(1.234) « parou na vírgula: virou "um vírgula dois três quatro"! float(1234.56) « certo
O (float) converte para número com casas (veja Conversão de Tipos). O caminho certo tem dois passos, nesta ordem:
- Tirar os pontos de milhar:
"1.234,56"→"1234,56". - Trocar a vírgula por ponto:
"1234,56"→"1234.56". Agora o PHP entende.
Se você fizer na ordem inversa, a vírgula vira ponto e depois some junto com os outros pontos. Em código real, você ainda precisa validar o que foi digitado (e se a pessoa digitar "abc"?). Isso é assunto de Validar Dados.
NumberFormatter: a extensão intl
O PHP tem uma extensão chamada intl (de internationalization, "internacionalização") que sabe as regras de cada país: moeda, separadores, até números por extenso. A classe NumberFormatter formata direto no padrão de um idioma:
<?php
$moeda = new NumberFormatter("pt_BR", NumberFormatter::CURRENCY);
echo $moeda->formatCurrency(1234.5, "BRL"), "\n";
$decimal = new NumberFormatter("pt_BR", NumberFormatter::DECIMAL);
echo $decimal->format(1234.567), "\n";
A extensão intl é grande (carrega as regras de todos os idiomas) e nem todo PHP a tem. O laboratório da Ana tem dois PHPs: o php de sempre, sem intl, e o php-completo, com intl. Veja a diferença:
$ php -m | grep intl
$ php intl.php
Fatal error: Uncaught Error: Class "NumberFormatter" not found in /home/ana/php/intl.php:2
Stack trace:
#0 {main}
thrown in /home/ana/php/intl.php on line 2
$ php-completo -m | grep intl
intl
$ php-completo intl.php
R$ 1.234,50
1.234,567
O php -m | grep intl não mostrou nada: sem a extensão, a classe não existe ("Classe NumberFormatter não encontrada"). Com intl, o valor sai no padrão brasileiro, com o R$ incluído.
- new NumberFormatter("pt_BR", …)
- Cria um "formatador" para o português do Brasil. O
newcria um objeto (assunto de Classes e Objetos). - NumberFormatter::CURRENCY
- O estilo: moeda (currency). Outros:
DECIMAL,PERCENT,SPELLOUT(por extenso). - ->formatCurrency(1234.5, "BRL")
- Formata o valor na moeda indicada pelo código internacional:
BRL(real),USD(dólar),EUR(euro).
Outras coisas que o intl sabe fazer:
<?php
$moeda = new NumberFormatter("pt_BR", NumberFormatter::CURRENCY);
echo $moeda->formatCurrency(1234.5, "USD"), "\n";
$us = new NumberFormatter("en_US", NumberFormatter::CURRENCY);
echo $us->formatCurrency(1234.5, "USD"), "\n";
$extenso = new NumberFormatter("pt_BR", NumberFormatter::SPELLOUT);
echo $extenso->format(1234), "\n";
$pct = new NumberFormatter("pt_BR", NumberFormatter::PERCENT);
echo $pct->format(0.875), "\n";
$decimal = new NumberFormatter("pt_BR", NumberFormatter::DECIMAL);
var_dump($decimal->parse("1.234,56"));
$ php-completo intl2.php US$ 1.234,50 « dólar, escrito do jeito brasileiro $1,234.50 « dólar, do jeito americano mil duzentos e trinta e quatro « por extenso (cheques, recibos) 88% « porcentagem: 0.875 vira 88% (arredondado) float(1234.56) « o caminho de volta: texto brasileiro → número
R$ não é um espaço comum
O intl coloca entre R$ e o número um espaço não separável (U+00A0, o mesmo da aula de mbstring), para o navegador nunca quebrar a linha ali. Ele parece igual, mas "R$ 1.234,50" === $moeda->formatCurrency(1234.5, "BRL") dá false. Cuidado ao comparar textos ou ao escrever testes.
O PHP que roda no navegador não tem a extensão intl, por isso não há caixa "Experimente" nesta seção: os exemplos acima foram rodados no php-completo do laboratório. Num servidor seu, instale com sudo apt install php-intl (Ubuntu/Debian; veja Instalar no Linux) e confira com php -m.
Para um site em português, number_format resolve 99% dos casos e funciona em qualquer PHP. O NumberFormatter vale a pena quando o mesmo site atende vários países.
Qual usar?
| Você quer… | Use | Resultado |
|---|---|---|
| Mostrar um valor no formato brasileiro | number_format($v, 2, ",", ".") | Texto 1.234,50 |
| Mostrar dinheiro | "R$ " . number_format($v, 2, ",", ".") | Texto R$ 1.234,50 |
| Um número arredondado, para continuar calculando | round($v, 2) | Número 1234.57 |
| Sempre para cima / para baixo | ceil / floor | Número |
| Ponto decimal garantido (CSV, JSON, APIs) | sprintf("%.2F", $v) ou number_format($v, 2, ".", "") | Texto 1234.50 |
| Zeros à esquerda, colunas alinhadas | sprintf | Texto 00042 |
| Vários países e moedas | NumberFormatter (intl) | Texto no padrão do país |
Quando dá errado
<?php
echo number_format("1234,5", 2, ",", "."), "\n";
$ php virgula.php
Fatal error: Uncaught TypeError: number_format(): Argument #1 ($num) must be of type int|float, string given in /home/ana/php/virgula.php:2
Stack trace:
#0 /home/ana/php/virgula.php(2): number_format('1234,5', 2, ',', '.')
#1 {main}
thrown in /home/ana/php/virgula.php on line 2
O que significa: o 1º argumento precisa ser um número (int ou float). Um texto como "1234.5" seria aceito (o PHP converte), mas "1234,5", com vírgula, não é um número para o PHP.
Como consertar: converta antes, como em o caminho de volta: (float) str_replace(",", ".", $valor).
$ php voltar.php float(1.234) float(1234.56)
O que significa: um texto no formato brasileiro ("1.234,56") foi convertido direto para número. O PHP leu o ponto do milhar como ponto decimal e parou na vírgula. Nenhum aviso aparece.
Como consertar: tire os pontos e troque a vírgula por ponto antes de converter. E nunca guarde o valor formatado: guarde o número.
O que significa: você tentou fazer conta com o resultado do number_format (que é texto). Veja o aviso na seção de dinheiro.
Como consertar: faça as contas com a variável numérica e chame number_format só no echo.
O que significa: a extensão intl não está instalada.
Como consertar: instale (sudo apt install php-intl no Ubuntu; veja Instalar no Linux) e confira com php -m. Ou use number_format, que é do núcleo do PHP e está sempre disponível.
Referência
| Função | O que faz | Exemplo → resultado |
|---|---|---|
number_format($n) | Inteiro, milhar com vírgula | number_format(1234.5) → "1,235" |
number_format($n, $casas) | Formato americano | number_format(1234.5, 2) → "1,234.50" |
number_format($n, $casas, $dec, $mil) | Você escolhe os separadores | number_format(1234.5, 2, ",", ".") → "1.234,50" |
round($n, $casas) | Arredonda (meio para longe do zero) | round(2.5) → 3.0 |
round($n, $casas, $modo) | Outro modo de arredondar | round(2.5, 0, PHP_ROUND_HALF_EVEN) → 2.0 |
ceil($n) | Para cima | ceil(4.1) → 5.0 |
floor($n) | Para baixo | floor(4.9) → 4.0 |
intval($n) | Corta as casas | intval(4.9) → 4 |
NumberFormatter (intl) | Formata conforme o país | Precisa da extensão intl |
Exercícios
1. Formato brasileiro
Complete para mostrar 9.876,50:
2. Vans para a excursão
Complete para calcular quantas vans de 15 lugares levam 47 alunos:
3. Qual o tipo do resultado de number_format(10, 2, ",", ".")?
4. O que var_dump((float) "2.500,00") mostra?
5. O que floor(-4.5) devolve?
Missão final: a cliente comprou 3 kg de maçã (R$ 4,50 o kg) e 2 kg de uva (R$ 7,90 o kg) e tem 10% de desconto. Mostre:Total: R$ 29,30Com desconto: R$ 26,37
Faça as contas com os números e use number_format só na hora do echo.
<?php $total = 3 * 4.5 + 2 * 7.9; $comDesconto = $total * 0.9; echo "Total: R$ $total\n"; echo "Com desconto: R$ $comDesconto\n";
<?php $total = 3 * 4.5 + 2 * 7.9; $comDesconto = $total * 0.9; echo "Total: R$ " . number_format($total, 2, ",", "."), "\n"; echo "Com desconto: R$ " . number_format($comDesconto, 2, ",", "."), "\n";
Resumo
- O número é para calcular; o texto formatado é para mostrar. Formate só no fim.
number_format($v, 2, ",", ".")→1.234,50. Para dinheiro, concatene o"R$ ".number_formatdevolve um texto: não faça contas com ele.roundarredonda (o meio vai para longe do zero),ceilsobe,floordesce,intvalcorta.- O
printfpode arredondar diferente nos "empates"; para dinheiro, useround/number_format, ou trabalhe em centavos. - Texto brasileiro para número: tire os pontos, troque a vírgula por ponto, converta com
(float). NumberFormatter(extensão intl) formata conforme o país, por extenso e em porcentagem; não está no editor do guia nem em todo PHP.