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.

Você vai aprender
  • number_format com 1, 2 e 4 argumentos, e o formato brasileiro 1.234,56.
  • Mostrar dinheiro: "R$ " . number_format(…).
  • Arredondar: round, ceil, floor, e por que o printf à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 NumberFormatter da 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.

Não faça contas com o texto formatado
<?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çãoOrigem do nomeO que faz4.14.54.9-4.5
round"arredondar"Para o mais próximo; o meio vai para longe do zero455-5
ceilceiling, "teto"Sempre para cima555-4
floor"chão"Sempre para baixo444-5
intvalinteger valueCorta as casas (vira int)444-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.

Outras formas de arredondar o meio

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.

Dinheiro "sério": trabalhe em centavos

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:

  1. Tirar os pontos de milhar: "1.234,56" → "1234,56".
  2. 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 new cria 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
O espaço depois do 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.

No editor do guia não há intl

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…UseResultado
Mostrar um valor no formato brasileironumber_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 calculandoround($v, 2)Número 1234.57
Sempre para cima / para baixoceil / floorNúmero
Ponto decimal garantido (CSV, JSON, APIs)sprintf("%.2F", $v) ou number_format($v, 2, ".", "")Texto 1234.50
Zeros à esquerda, colunas alinhadassprintfTexto 00042
Vários países e moedasNumberFormatter (intl)Texto no padrão do país

Quando dá errado

number_format(): Argument #1 ($num) must be of type int|float, string given
<?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).

O valor ficou mil vezes menor
$ 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.

Unsupported operand types: string + int

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.

Class "NumberFormatter" not found

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çãoO que fazExemplo → resultado
number_format($n)Inteiro, milhar com vírgulanumber_format(1234.5) → "1,235"
number_format($n, $casas)Formato americanonumber_format(1234.5, 2) → "1,234.50"
number_format($n, $casas, $dec, $mil)Você escolhe os separadoresnumber_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 arredondarround(2.5, 0, PHP_ROUND_HALF_EVEN) → 2.0
ceil($n)Para cimaceil(4.1) → 5.0
floor($n)Para baixofloor(4.9) → 4.0
intval($n)Corta as casasintval(4.9) → 4
NumberFormatter (intl)Formata conforme o paísPrecisa da extensão intl

Exercícios

1. Formato brasileiro

Complete para mostrar 9.876,50:

echo number_format(9876.5, );

2. Vans para a excursão

Complete para calcular quantas vans de 15 lugares levam 47 alunos:

$vans = (47 / 15);

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,30
Com 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_format devolve um texto: não faça contas com ele.
  • round arredonda (o meio vai para longe do zero), ceil sobe, floor desce, intval corta.
  • O printf pode arredondar diferente nos "empates"; para dinheiro, use round/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.