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.
- O que é um manipulador e onde ele entra no
cout. - Largura de coluna com
std::setwe o caractere de preenchimento comstd::setfill. - Alinhar à esquerda ou à direita com
std::leftestd::right. - Dinheiro com duas casas:
std::fixed+std::setprecision(2), e o que osetprecisionfaz sem ofixed. true/falsepor extenso comstd::boolalpha, e números em hexadecimal comstd::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
42saiu grudado: osetwvale 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 como00042, 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ção | O que setprecision(2) significa | 19.9 vira |
|---|---|---|
Padrão (sem fixed) | 2 algarismos no total (algarismos significativos) | 20 |
Com std::fixed | 2 casas depois do ponto | 19.90 |
- std::fixed
- "Ponto fixo": nunca usa notação científica (
1e+06) e faz osetprecisioncontar as casas decimais. Sem ele,setprecision(2)transformou19.9em20: um susto numa nota fiscal. - std::setprecision(2)
- "Defina a precisão". Arredonda na hora de mostrar:
1234.5678apareceu como1234.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);.
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
dece todos os números seguintes do programa sairão em hexadecimal, como oa 10acima. - std::showbase
- Acrescenta o prefixo da base:
0xno hexadecimal,0no 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
| Manipulador | Gruda? | Como desfazer |
|---|---|---|
std::setw(n) | Não: vale só para o próximo valor | (não precisa) |
std::setfill(c) | Sim | std::setfill(' ') |
std::left / std::right | Sim | O outro (std::right é o padrão) |
std::fixed | Sim | std::defaultfloat |
std::setprecision(n) | Sim | std::setprecision(6) (o padrão) |
std::boolalpha | Sim | std::noboolalpha |
std::hex / std::oct | Sim | std::dec |
std::showbase | Sim | std::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
- Cabeçalho: "Aluno" à esquerda numa coluna de 10; "Nota" à direita numa coluna de 8. Total: 18 caracteres.
- 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. - Uma casa decimal, configurada uma vez só:
fixedesetprecisiongrudam, e é isso que queremos aqui. - Cada linha repete
left/setw(10)para o nome eright/setw(8)para a nota, porquesetwnã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
| Forma | Exemplo | Quando 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/23 | std::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.
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);.
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.
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
| Manipulador | Cabeçalho | O que faz | Exemplo → saída |
|---|---|---|---|
std::setw(n) | <iomanip> | Largura mínima do próximo valor | setw(5) << 42 → 42 |
std::setfill(c) | <iomanip> | Caractere de preenchimento | setfill('0') << setw(5) << 42 → 00042 |
std::left, std::right | <iostream> | Lado do alinhamento | left << 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 algarismos | fixed << setprecision(2) << 19.9 → 19.90 |
std::defaultfloat | <iostream> | Desfaz o fixed | |
std::boolalpha | <iostream> | bool como true/false | boolalpha << true → true |
std::hex, std::oct, std::dec | <iostream> | Base 16, 8 ou 10 | hex << 255 → ff |
std::showbase | <iostream> | Mostra 0x / 0 | hex << showbase << 255 → 0xff |
Exercícios
1. Dinheiro
Complete para mostrar preco com duas casas decimais:
2. Zeros à esquerda
Complete para mostrar o pedido 87 como 000087:
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). Semfixed,setprecisionconta algarismos no total (19.9→20). std::boolalpha:true/false.std::hex: base 16; volte comstd::dec.- Quase todos os manipuladores grudam até você desfazê-los. O
setwconta bytes, então acentos desalinham colunas.