C++ Formatar com iomanip

O std::cout mostra os números do jeito dele: 19.9 em vez de 19.90, 1.41421 com seis algarismos, colunas tortas quando os nomes têm tamanhos diferentes. Para uma lojinha ou um boletim, isso não serve. A biblioteca <iomanip> traz os manipuladores: instruções que você coloca no meio do cout para dizer como mostrar o que vem depois. Nesta aula você vai alinhar colunas, mostrar dinheiro com duas casas e montar a tabela de notas da turma da Ana.

Você vai aprender
  • O que é um manipulador e onde ele entra no cout.
  • Largura de coluna com std::setw e o caractere de preenchimento com std::setfill.
  • Alinhar à esquerda ou à direita com std::left e std::right.
  • Dinheiro com duas casas: std::fixed + std::setprecision(2), e o que o setprecision faz sem o fixed.
  • true/false por extenso com std::boolalpha, e números em hexadecimal com std::hex.
  • Por que quase todos os manipuladores "grudam" no cout, e o único que não gruda.

Todos os programas desta aula foram compilados com o g++ 15.2 na máquina fictícia da Ana (usuária ana, computador notebook), com g++ -std=c++23 -Wall -Wextra arquivo.cpp -o programa. Nas saídas, os colchetes [ ] foram impressos de propósito, para você enxergar os espaços.


A ideia: as configurações da impressora

Antes de imprimir um documento, você escolhe: margem, tamanho da letra, quantas cópias. Essas escolhas valem para tudo que for impresso depois, até você mudá-las de novo. Os manipuladores são as configurações do cout: você as "envia" com <<, como se fossem um valor, mas em vez de aparecer na tela, elas mudam o jeito como os próximos valores aparecem.

std::cout << std::fixed << std::setprecision(2) << 19.9;   // mostra 19.90

std::fixed e std::setprecision(2) não aparecem na tela: eles configuram. Só o 19.9 é mostrado, já do jeito configurado. Os manipuladores sem parênteses (std::fixed, std::left, std::hex) vêm de <iostream>; os que recebem um valor entre parênteses (std::setw(8), std::setprecision(2), std::setfill('0')) precisam de #include <iomanip> (input/output manipulators, manipuladores de entrada e saída). Para não se preocupar, inclua sempre os dois.


1. Largura: std::setw

setw vem de set width, "defina a largura". Ele reserva um espaço de N caracteres para o próximo valor. Se o valor for menor, o resto é preenchido com espaços:

#include <iostream>
#include <iomanip>

int main() {
    std::cout << "[" << std::setw(8) << 42 << "]\n";
    std::cout << "[" << std::setw(8) << "Ana" << "]\n";
    std::cout << "[" << std::setw(8) << 42 << 42 << "]\n";
    std::cout << "[" << std::setw(2) << 12345 << "]\n";
}
$ g++ -std=c++23 -Wall -Wextra largura.cpp -o largura
$ ./largura
[      42]               « 6 espaços + "42" = 8 caracteres
[     Ana]               « 5 espaços + "Ana" = 8
[      4242]             « o setw só valeu para o PRIMEIRO 42
[12345]                  « largura pequena demais: o número não é cortado
std::setw(8)
"O próximo valor ocupa pelo menos 8 caracteres." Por padrão, os espaços vão à esquerda (o valor fica encostado à direita).
linha 3
O segundo 42 saiu grudado: o setw vale para um valor só e depois é esquecido. Ele é a grande exceção da regra "manipuladores grudam" (seção 6).
linha 4
setw é uma largura mínima. Se o valor não cabe, ele aparece inteiro, e a coluna fica torta, mas nenhum dígito se perde.

2. Alinhar e preencher: left, right e setfill

Textos (como nomes) costumam ficar alinhados à esquerda, e números, à direita, para que as unidades fiquem uma embaixo da outra. Para escolher o lado, use std::left ou std::right. Para trocar o espaço por outro caractere, std::setfill:

#include <iostream>
#include <iomanip>

int main() {
    std::cout << "[" << std::left << std::setw(8) << "Ana" << "]\n";
    std::cout << "[" << std::setw(8) << "Bruno" << "]\n";
    std::cout << "[" << std::right << std::setw(8) << "Ana" << "]\n";
    std::cout << "[" << std::setfill('.') << std::setw(8) << "Ana" << "]\n";
    std::cout << "[" << std::setfill('0') << std::setw(5) << 42 << "]\n";
    std::cout << "[" << std::setw(5) << 7 << "]\n";
}
$ ./lado
[Ana     ]               « left: texto à esquerda, espaços à direita
[Bruno   ]               « o left continuou valendo (gruda!)
[     Ana]               « right: volta ao padrão
[.....Ana]               « setfill('.'): pontinhos no lugar dos espaços
[00042]                  « setfill('0') + setw(5): zeros à esquerda
[00007]                  « o '0' continuou valendo
std::left
"Alinhe à esquerda": o valor primeiro, o preenchimento depois.
std::right
"Alinhe à direita" (o padrão).
std::setfill('.')
Preenche com . em vez de espaço. Repare nas aspas simples: é um caractere (char, aula Caracteres), não um texto. Com '0', faz números como 00042, comuns em códigos de pedido e matrículas.

Experimente: troque o '*' por '-', mude a largura e alterne entre std::left e std::right. Repare que o número do pedido usa outro preenchimento.

#include <iostream>
#include <iomanip>

int main() {
    std::cout << std::setfill('*') << std::left << std::setw(20) << "Lojinha da Ana" << "\n";
    std::cout << "Pedido: " << std::setfill('0') << std::right << std::setw(6) << 87 << "\n";
}

3. Casas decimais: std::fixed e std::setprecision

Este é o manipulador mais usado: mostrar dinheiro com duas casas. Ele tem uma pegadinha, então veja os três jeitos lado a lado:

#include <iostream>
#include <iomanip>

int main() {
    double preco = 19.9;
    double pi = 3.14159;
    std::cout << preco << " " << pi << "\n";
    std::cout << std::setprecision(2) << preco << " " << pi << "\n";
    std::cout << std::fixed << std::setprecision(2) << preco << " " << pi << "\n";
    std::cout << 7.0 << " " << 1234.5678 << "\n";
}
$ ./dinheiro
19.9 3.14159             « padrão: até 6 algarismos, sem zeros no fim
20 3.1                   « setprecision(2) SOZINHO: 2 algarismos no total!
19.90 3.14               « fixed + setprecision(2): 2 casas depois do ponto
7.00 1234.57             « continua valendo nas linhas seguintes
ConfiguraçãoO que setprecision(2) significa19.9 vira
Padrão (sem fixed)2 algarismos no total (algarismos significativos)20
Com std::fixed2 casas depois do ponto19.90
std::fixed
"Ponto fixo": nunca usa notação científica (1e+06) e faz o setprecision contar as casas decimais. Sem ele, setprecision(2) transformou 19.9 em 20: um susto numa nota fiscal.
std::setprecision(2)
"Defina a precisão". Arredonda na hora de mostrar: 1234.5678 apareceu como 1234.57. O valor guardado na variável não muda; só a exibição.

Regra prática: para dinheiro, sempre os dois juntos: std::cout << std::fixed << std::setprecision(2);.

Por que 2.675 aparece como 2.67, e não 2.68?
#include <iostream>
#include <iomanip>

int main() {
    std::cout << std::fixed << std::setprecision(2);
    std::cout << 2.675 << "\n";
    std::cout << 1.005 << "\n";
    std::cout << std::setprecision(20) << 2.675 << "\n";
}
$ ./arred
2.67
1.00
2.67499999999999982236   « o que está guardado de verdade na variável

O computador guarda números com vírgula em binário, e 2.675 não tem representação exata: o valor guardado é um pouquinho menor, 2.67499999…. Arredondado, dá 2.67. A aula float e double explica o porquê. Para sistemas que lidam com dinheiro de verdade, os programadores costumam guardar os valores em centavos, num inteiro (int ou long long), e só formatar na hora de mostrar.

Missão: a lista de compras da Ana mostra 4.5 e 12. Faça aparecer R$ 4.50 e R$ 12.00, acrescentando uma linha com os manipuladores certos.

#include <iostream>
#include <iomanip>

int main() {
    double arroz = 4.5;
    double feijao = 12;
    std::cout << "Arroz: R$ " << arroz << "\n";
    std::cout << "Feijão: R$ " << feijao << "\n";
}
#include <iostream>
#include <iomanip>

int main() {
    double arroz = 4.5;
    double feijao = 12;
    std::cout << std::fixed << std::setprecision(2);
    std::cout << "Arroz: R$ " << arroz << "\n";
    std::cout << "Feijão: R$ " << feijao << "\n";
}

Repare que o cout sempre usa ponto como separador decimal (4.50), o jeito dos Estados Unidos. Mostrar 4,50 é possível configurando o "local" (locale) do programa; a aula std::format e std::print mostra como e quais cuidados tomar.


4. true e false por extenso: std::boolalpha

Por padrão, o cout mostra um bool (aula Booleanos) como 1 ou 0. Com std::boolalpha ("booleano em letras"), mostra true ou false:

#include <iostream>
#include <iomanip>

int main() {
    bool aprovada = true;
    std::cout << aprovada << "\n";
    std::cout << std::boolalpha << aprovada << " " << (2 > 3) << "\n";
    std::cout << std::noboolalpha << aprovada << "\n";

    std::cout << std::hex << 255 << "\n";
    std::cout << 10 << " " << 16 << "\n";
    std::cout << std::showbase << 255 << "\n";
    std::cout << std::dec << 255 << "\n";
}
$ ./boolhex
1                        « padrão: 1 = verdadeiro
true false               « boolalpha: por extenso
1                        « noboolalpha: de volta ao 1/0
ff                       « 255 em hexadecimal
a 10                     « o hex GRUDOU: 10 virou "a" e 16 virou "10"
0xff                     « showbase: mostra o prefixo 0x
255                      « dec: de volta ao decimal

5. Outras bases: std::hex, std::oct, std::dec

O hexadecimal (base 16) usa os dígitos 0–9 e as letras a–f (a = 10, … f = 15). Ele aparece em cores de páginas web (#ff8800), endereços de memória e códigos de erro. No exemplo acima:

std::hex
Mostra os inteiros em base 16: 255 → ff.
std::oct
Base 8 (octal), usada nas permissões de arquivos do Linux (755).
std::dec
Volta à base 10, o padrão. Esqueça o dec e todos os números seguintes do programa sairão em hexadecimal, como o a 10 acima.
std::showbase
Acrescenta o prefixo da base: 0x no hexadecimal, 0 no octal.

6. Os manipuladores grudam

Você já viu nas saídas: depois de um std::fixed, um std::left ou um std::hex, tudo o que vem depois segue a configuração, até no fim do programa. Isso pega muita gente de surpresa:

#include <iostream>
#include <iomanip>

int main() {
    double media = 8.0;
    std::cout << "Preço: " << std::fixed << std::setprecision(2) << 4.5 << "\n";
    std::cout << "Média: " << media << "\n";
    std::cout << "Pi: " << 3.14159 << "\n";
    std::cout << std::defaultfloat << std::setprecision(6);
    std::cout << "Pi: " << 3.14159 << "\n";
}
$ ./grudam
Preço: 4.50
Média: 8.00              « a Ana só queria as 2 casas no preço…
Pi: 3.14                 « …mas o pi também perdeu casas
Pi: 3.14159              « defaultfloat + setprecision(6): de volta ao padrão
ManipuladorGruda?Como desfazer
std::setw(n)Não: vale só para o próximo valor(não precisa)
std::setfill(c)Simstd::setfill(' ')
std::left / std::rightSimO outro (std::right é o padrão)
std::fixedSimstd::defaultfloat
std::setprecision(n)Simstd::setprecision(6) (o padrão)
std::boolalphaSimstd::noboolalpha
std::hex / std::octSimstd::dec
std::showbaseSimstd::noshowbase

Por que o setw é diferente? Porque uma largura raramente serve para dois valores seguidos: cada coluna de uma tabela tem a sua. Por isso você repete o setw antes de cada valor, como na tabela a seguir.


7. Juntando tudo: a tabela de notas da Ana

A professora Ana quer imprimir as notas da turma com os nomes alinhados à esquerda e as notas alinhadas à direita, com uma casa decimal:

#include <iostream>
#include <iomanip>

int main() {
    std::cout << std::left << std::setw(10) << "Aluno"
              << std::right << std::setw(8) << "Nota" << "\n";
    std::cout << std::setfill('-') << std::setw(18) << "" << std::setfill(' ') << "\n";

    std::cout << std::fixed << std::setprecision(1);
    std::cout << std::left << std::setw(10) << "Ana"
              << std::right << std::setw(8) << 9.5 << "\n";
    std::cout << std::left << std::setw(10) << "Bruno"
              << std::right << std::setw(8) << 7.25 << "\n";
    std::cout << std::left << std::setw(10) << "Carla"
              << std::right << std::setw(8) << 10.0 << "\n";
}
$ g++ -std=c++23 -Wall -Wextra notas.cpp -o notas
$ ./notas
Aluno         Nota
------------------
Ana            9.5
Bruno          7.2      « 7.25 com 1 casa: empate exato, vai para o par (7.2)
Carla         10.0
  1. Cabeçalho: "Aluno" à esquerda numa coluna de 10; "Nota" à direita numa coluna de 8. Total: 18 caracteres.
  2. A linha de traços: um truque comum. setfill('-') + setw(18) + um texto vazio "" = 18 traços. Logo depois, setfill(' ') volta ao espaço, senão os traços apareceriam nas linhas seguintes.
  3. Uma casa decimal, configurada uma vez só: fixed e setprecision grudam, e é isso que queremos aqui.
  4. Cada linha repete left/setw(10) para o nome e right/setw(8) para a nota, porque setw não gruda e o lado muda a cada coluna.

O 7.25 virou 7.2, e não 7.3: diferente do 2.675, o 7.25 é guardado exato em binário, e quando o arredondamento cai exatamente no meio, a biblioteca do Linux escolhe o algarismo par (o "arredondamento do banqueiro"). Se a nota precisa ser arredondada "para cima no meio", arredonde o valor antes, com std::round(nota * 10) / 10 (aula Matemática).

Quando a tabela crescer, você não vai escrever uma linha por aluno: vai guardar os nomes numa lista e usar uma repetição (aulas std::vector e for).


Formas diferentes + qual usar

FormaExemploQuando usar
cout + <iomanip>std::cout << std::fixed << std::setprecision(2) << x;Funciona em qualquer versão do C++. Você vai encontrar em quase todo código existente.
std::format / std::print C++20/23std::println("{:.2f}", x);Mais curto, e a configuração não gruda. Preferível em código novo. Aula std::format e std::print.
printf (do C)std::printf("%.2f\n", x);Código antigo. O compilador nem sempre confere se os tipos batem com o formato.

Quando dá errado

error: ‘setw’ is not a member of ‘std’
#include <iostream>

int main() {
    std::cout << std::setw(8) << 42 << "\n";
}
$ g++ -std=c++23 -Wall -Wextra semiomanip.cpp -o semiomanip
semiomanip.cpp: In function ‘int main()’:
semiomanip.cpp:4:23: error: ‘setw’ is not a member of ‘std’
    4 |     std::cout << std::setw(8) << 42 << "\n";
      |                       ^~~~
semiomanip.cpp:2:1: note: ‘std::setw’ is defined in header ‘<iomanip>’; this is probably fixable by adding ‘#include <iomanip>’
    1 | #include <iostream>
  +++ |+#include <iomanip>
    2 |

O que significa: "setw não é membro de std". A nota logo abaixo já dá a resposta: "std::setw é definido no cabeçalho <iomanip>; provavelmente se resolve acrescentando #include <iomanip>". As linhas com +++ |+ mostram exatamente onde pôr a linha nova.

Como consertar: acrescente #include <iomanip>. O mesmo vale para setprecision e setfill.

O preço sai 20 em vez de 19.90

O que significa: você usou std::setprecision(2) sem std::fixed. Sem ele, a precisão conta os algarismos no total.

Como consertar: std::cout << std::fixed << std::setprecision(2);.

Números estranhos depois de um hex, ou casas decimais onde não deviam

O que significa: um manipulador usado lá em cima continua valendo (eles grudam).

Como consertar: desfaça depois de usar: std::dec, std::defaultfloat, std::setprecision(6), std::setfill(' '), std::right. Ou use std::format, que não tem esse problema.

A coluna fica torta nos nomes com acento
    std::cout << std::left << std::setw(10) << "Ana" << "|\n";
    std::cout << std::left << std::setw(10) << "João" << "|\n";
    std::cout << std::left << std::setw(10) << "Lúcia Inês" << "|\n";
$ ./acento
Ana       |
João     |               « um espaço a menos
Lúcia Inês|              « dois a menos: a barra encostou

O que significa: o setw conta bytes, não letras. Em UTF-8, cada letra acentuada (ã, ú, ê) ocupa 2 bytes. Para o setw, "João" tem 5 "caracteres" e "Lúcia Inês" tem 12. A aula Acentos e UTF-8 explica a codificação.

Como consertar: nas tabelas, prefira cabeçalhos sem acento ou deixe folga na largura. O std::format (C++20) calcula a largura pela aparência das letras e alinha certo (veja a aula std::format e std::print).


Referência rápida

ManipuladorCabeçalhoO que fazExemplo → saída
std::setw(n)<iomanip>Largura mínima do próximo valorsetw(5) << 42 →    42
std::setfill(c)<iomanip>Caractere de preenchimentosetfill('0') << setw(5) << 42 → 00042
std::left, std::right<iostream>Lado do alinhamentoleft << setw(5) << "Ana" → Ana  
std::fixed<iostream>Casas decimais fixas, sem notação científica
std::setprecision(n)<iomanip>Com fixed: n casas. Sem: n algarismosfixed << setprecision(2) << 19.9 → 19.90
std::defaultfloat<iostream>Desfaz o fixed
std::boolalpha<iostream>bool como true/falseboolalpha << true → true
std::hex, std::oct, std::dec<iostream>Base 16, 8 ou 10hex << 255 → ff
std::showbase<iostream>Mostra 0x / 0hex << showbase << 255 → 0xff

Exercícios

1. Dinheiro

Complete para mostrar preco com duas casas decimais:

std::cout << std:: << std::setprecision() << preco;

2. Zeros à esquerda

Complete para mostrar o pedido 87 como 000087:

std::cout << std::setfill() << std::setw(6) << 87;

3. O que mostra std::cout << std::setprecision(3) << 3.14159; (sem fixed)?

4. Qual manipulador vale só para o valor seguinte?

5. Depois de std::cout << std::hex << 255;, o programa faz std::cout << 12;. O que aparece?

Missão final: o boletim alinhado

Missão: faça o boletim sair assim, com o nome à esquerda numa coluna de 8 e a nota à direita numa coluna de 6, sempre com uma casa decimal:

Ana        9.5
Bruno      7.0
Davi      10.0
#include <iostream>
#include <iomanip>

int main() {
    std::cout << "Ana" << 9.5 << "\n";
    std::cout << "Bruno" << 7.0 << "\n";
    std::cout << "Davi" << 10.0 << "\n";
}
#include <iostream>
#include <iomanip>

int main() {
    std::cout << std::fixed << std::setprecision(1);
    std::cout << std::left << std::setw(8) << "Ana" << std::right << std::setw(6) << 9.5 << "\n";
    std::cout << std::left << std::setw(8) << "Bruno" << std::right << std::setw(6) << 7.0 << "\n";
    std::cout << std::left << std::setw(8) << "Davi" << std::right << std::setw(6) << 10.0 << "\n";
}

Resumo

  • Manipuladores configuram o cout: você os envia com <<, eles não aparecem na tela. Os que têm parênteses precisam de #include <iomanip>.
  • std::setw(n): largura mínima do próximo valor (não gruda). std::setfill(c): troca o espaço por outro caractere.
  • std::left/std::right: lado do alinhamento. Nomes à esquerda, números à direita.
  • Dinheiro: std::fixed << std::setprecision(2). Sem fixed, setprecision conta algarismos no total (19.9 → 20).
  • std::boolalpha: true/false. std::hex: base 16; volte com std::dec.
  • Quase todos os manipuladores grudam até você desfazê-los. O setw conta bytes, então acentos desalinham colunas.