AI InterviewSearch…Search problems, topics, or pages…Ctrl KGet started
Skip to content

05 Strings / Lesson 5 of 6

Formatting output

14 min · +10 XP · 2 exercises · 7 questions

Read the text14 min
Exercises0/2
Quiz7 q

Quick answer

How do you format output in C++?

C++ formats output in two ways. The <iomanip> stream manipulators — std::setw, std::left, std::setfill, std::fixed, std::setprecision — change how the following values print, and all but setw stay in force until changed. C++20's std::format("{:>8.2f}", x) puts width, alignment and precision in one placeholder, returns a std::string, and leaves nothing sticky.

Output that must match a judge or line up in a report is a formatting problem before anything else: how wide is the column, which side is padded, how many decimals, zero-filled or hex. C++ has two toolkits. The stream manipulators in <iomanip> have been there from the start and are what most existing code uses; C++20's std::format says the same things in one compact specification and is the one for new code. This lesson settles both — including which stream settings are sticky and which apply once, the mistake that bites everyone — and shows how to build a table, and a string, before printing anything.

Width, alignment and fill

#include <iomanip>
#include <iostream>

int main() {
    std::cout << '[' << std::setw(6) << 42 << "]\n";                     // [    42]
    std::cout << '[' << std::setw(6) << std::left << 42 << "]\n";        // [42    ]
    std::cout << '[' << std::setw(6) << "ab" << "]\n";                   // [ab    ] - left is still on
    std::cout << std::right << std::setfill('0') << std::setw(5) << 7 << '\n';   // 00007
    std::cout << std::setw(3) << 1 << std::setw(3) << 2 << '\n';         // 001002 - fill is still '0'
    return 0;
}

std::setw(n) sets the minimum width of the next insertion only; the one after it is back to width 0. std::left, std::right and std::setfill(c) stay in force until changed, which is why the last line above zero-fills both numbers. Numbers and strings alike default to right alignment on a stream. A value wider than the field is never truncated; the field grows.

Precision and the floating-point modes

double x = 3.14159265;
std::cout << std::setprecision(3) << x << '\n';                       // 3.14     - 3 SIGNIFICANT digits
std::cout << std::setprecision(3) << 1234.5678 << '\n';               // 1.23e+03 - still 3 significant digits
std::cout << std::fixed << std::setprecision(3) << x << '\n';         // 3.142    - 3 digits AFTER the point
std::cout << std::fixed << std::setprecision(2) << 2.0 / 3 << '\n';   // 0.67
std::cout << std::scientific << x << '\n';                            // 3.14e+00 - precision 2 still applies
std::cout << std::defaultfloat << std::setprecision(6) << x << '\n';  // 3.14159  - back to the default

Without std::fixed, setprecision counts significant digits and the stream may switch to scientific notation for large or small values — hence 1.2e+03 for 1234.5 at setprecision(2). With std::fixed it counts digits after the point, the mode every exercise with money or averages wants. Both are sticky; set them once at the top of main. Rounding is to nearest, so 2.0 / 3 at two decimals is 0.67, while an exact tie such as 0.125 rounds to even and prints 0.12 — never build an expectation on a tie.

Integers have their own modes: std::hex, std::oct and std::dec change the base and stay changed, std::showbase adds 0x, std::uppercase capitalises the digits. Forgetting to switch back to std::dec is a classic: every integer printed afterwards comes out in hex.

Sticky or one-shot

ManipulatorEffectLasts
std::setw(n)minimum widthnext insertion only
std::left / std::right / std::internalalignment within the widthuntil changed
std::setfill(c)padding characteruntil changed
std::fixed / std::scientific / std::defaultfloatfloating-point modeuntil changed
std::setprecision(n)digits (meaning depends on the mode)until changed
std::hex / std::oct / std::decinteger baseuntil changed
std::boolalphatrue/false instead of 1/0until changed

std::format

#include <format>

std::cout << std::format("{} scored {}\n", "Ada", 97);                        // Ada scored 97
std::cout << std::format("[{:>6}] [{:<6}] [{:^6}]\n", 42, 42, 42);            // [    42] [42    ] [  42  ]
std::cout << std::format("[{:6}] [{:6}]\n", 42, "ab");                        // [    42] [ab    ] - numbers right, text left
std::cout << std::format("{:05} {:+d} {:.3f} {:8.2f}\n", 42, 7, 3.14159, 2.5); // 00042 +7 3.142     2.50
std::cout << std::format("{:x} {:X} {:#x} {:08b} {:o}\n", 255, 255, 255, 5, 8); // ff FF 0xff 00000101 10
std::cout << std::format("{1} before {0}\n", "b", "a");                       // a before b
std::cout << std::format("{:*^11}\n", "mid");                                 // ****mid****

Each {} is replaced by the next argument; after a colon comes a specification in a fixed order: fill and align (< left, > right, ^ centre, optionally preceded by the fill character), sign (+), # for a base prefix, 0 for zero padding, width, .precision, and a type letter — d, x, X, b, o for integers, f, e, g for floating point, s for strings. Everything is optional; {:8.2f} reads "width 8, two decimals, fixed". Unlike streams, nothing is sticky: each placeholder says everything about itself, and the default alignment differs by kind — numbers right, text left, which is exactly what a table wants.

The format string must be known at compile time, because the compiler checks the placeholders against the argument types: {:d} with a std::string is a compile error, not a runtime surprise. For a string built at run time use std::vformat(fmt, std::make_format_args(args...)). std::format returns a std::string; std::print (C++23) writes straight to stdout but is not available on this runtime — write std::cout << std::format(...).

Building a string first

#include <sstream>

std::ostringstream out;
out << std::fixed << std::setprecision(1);
for (int i = 1; i <= 3; ++i) out << i << ':' << i * 0.5 << ' ';
std::string text = out.str();       // "1:0.5 2:1.0 3:1.5 "
std::cout << text << '\n';

An std::ostringstream accepts everything std::cout does and hands the result back with str(): assemble a line before deciding whether to print it, measure a string's width before padding, or collect a large output and write it once. std::format covers the simple cases since it already returns a string; the stream wins when many pieces with shared settings accumulate in a loop.

A table, done right

struct Row { std::string name; int qty; double price; };

void print_table(const std::vector<Row>& rows) {
    std::cout << std::format("{:<10}{:>5}{:>10}\n", "item", "qty", "price");
    for (const Row& r : rows) {
        std::cout << std::format("{:<10}{:>5}{:>10.2f}\n", r.name, r.qty, r.price);
    }
}
item        qty     price
pen           3      1.20
notebook     12     45.00

The rules that keep a table clean and a judge happy: right-align numbers so decimal points line up; put the left-aligned text column first so no line ends in padding; give each column a width that fits its widest value and use the same widths for the header. This judge ignores trailing spaces, but a left-aligned last column still produces them and other judges compare bytes — end each row with a right-aligned column and the problem never arises.

Pitfalls

  • setw is one-shot; setprecision, fixed, left, setfill and hex are sticky and stay on for the rest of the program.
  • setprecision(2) without fixed means two significant digits: 1234.5 prints as 1.2e+03.
  • std::hex left on turns every later integer into hex; switch back with std::dec.
  • The std::format string must be a compile-time constant; a std::string variable does not compile — use std::vformat.
  • A left-aligned last column pads with spaces to the end of the line; put text columns first.
  • Ties such as 0.125 round to even at two decimals; do not build an expectation on one.

Key takeaways

  • Streams: setw applies once; alignment, fill, precision, floating mode and base persist.
  • std::fixed plus setprecision(n) is the deterministic way to print n decimals; without fixed, n counts significant digits.
  • std::format("{:>8.2f}") reads fill/align, sign, #, 0, width, precision, type — nothing is sticky, numbers right-align and text left-aligns by default.
  • The format string is checked at compile time; it must be a literal.
  • std::ostringstream collects output into a string via str(); std::format returns one directly.
  • Put text columns first and right-align numbers so no line ends in padding.

Common questions

What does setw do in C++?

std::setw(n), from <iomanip>, sets the minimum width of the next value inserted into a stream, padding it with the fill character; the value after it is back to width 0. A value wider than the field is never truncated. Alignment comes from std::left or std::right, which stay in force.

What is the difference between setprecision with and without fixed?

Without std::fixed, std::setprecision(n) counts significant digits and may switch to scientific notation, so 1234.5 at precision 2 prints 1.2e+03. With std::fixed it counts digits after the decimal point, so std::fixed << std::setprecision(2) prints exactly two decimals. Both settings are sticky.

How does std::format work in C++?

std::format (C++20, <format>) replaces each {} in a compile-time format string with the next argument and returns a std::string. After a colon a placeholder can set fill and alignment, sign, width, precision and type: {:>8.2f} is right-aligned, width 8, two decimals. Nothing is sticky, and mismatched types are compile errors.

Which iomanip manipulators are sticky?

Only std::setw is one-shot, applying to the next insertion alone. std::left, std::right, std::setfill, std::fixed, std::scientific, std::setprecision, std::hex, std::dec and std::boolalpha stay in force until changed — so a forgotten std::hex prints every later integer in hexadecimal.

How do you print an aligned table in C++?

Give each column a fixed width that fits its widest value, left-align text and right-align numbers so decimal points line up — for example std::format("{:<10}{:>5}{:>10.2f}", name, qty, price). Put text columns first and end each row with a right-aligned column, so no line ends in padding.

Mark as readSign in to keep your place.

C++ Output Formatting: setw, setprecision and std::format

Format C++ output with iomanip manipulators or C++20 std::format: width, alignment, fill, fixed decimals and hex. Which settings stick, and aligning a table.

  • Course: C++ study plan
  • Module: Strings
  • Kind: Lesson
  • Reading time: 14 min
  • Runtime: Clang 18 · C++20

How do you format output in C++?

C++ formats output in two ways. The <iomanip> stream manipulators — std::setw, std::left, std::setfill, std::fixed, std::setprecision — change how the following values print, and all but setw stay in force until changed. C++20's std::format("{:>8.2f}", x) puts width, alignment and precision in one placeholder, returns a std::string, and leaves nothing sticky.

Lesson

Output that must match a judge or line up in a report is a formatting problem before anything else: how wide is the column, which side is padded, how many decimals, zero-filled or hex. C++ has two toolkits. The stream manipulators in <iomanip> have been there from the start and are what most existing code uses; C++20's std::format says the same things in one compact specification and is the one for new code. This lesson settles both — including which stream settings are sticky and which apply once, the mistake that bites everyone — and shows how to build a table, and a string, before printing anything.

Width, alignment and fill

#include <iomanip>
#include <iostream>

int main() {
    std::cout << '[' << std::setw(6) << 42 << "]\n";                     // [    42]
    std::cout << '[' << std::setw(6) << std::left << 42 << "]\n";        // [42    ]
    std::cout << '[' << std::setw(6) << "ab" << "]\n";                   // [ab    ] - left is still on
    std::cout << std::right << std::setfill('0') << std::setw(5) << 7 << '\n';   // 00007
    std::cout << std::setw(3) << 1 << std::setw(3) << 2 << '\n';         // 001002 - fill is still '0'
    return 0;
}

std::setw(n) sets the minimum width of the next insertion only; the one after it is back to width 0. std::left, std::right and std::setfill(c) stay in force until changed, which is why the last line above zero-fills both numbers. Numbers and strings alike default to right alignment on a stream. A value wider than the field is never truncated; the field grows.

Precision and the floating-point modes

double x = 3.14159265;
std::cout << std::setprecision(3) << x << '\n';                       // 3.14     - 3 SIGNIFICANT digits
std::cout << std::setprecision(3) << 1234.5678 << '\n';               // 1.23e+03 - still 3 significant digits
std::cout << std::fixed << std::setprecision(3) << x << '\n';         // 3.142    - 3 digits AFTER the point
std::cout << std::fixed << std::setprecision(2) << 2.0 / 3 << '\n';   // 0.67
std::cout << std::scientific << x << '\n';                            // 3.14e+00 - precision 2 still applies
std::cout << std::defaultfloat << std::setprecision(6) << x << '\n';  // 3.14159  - back to the default

Without std::fixed, setprecision counts significant digits and the stream may switch to scientific notation for large or small values — hence 1.2e+03 for 1234.5 at setprecision(2). With std::fixed it counts digits after the point, the mode every exercise with money or averages wants. Both are sticky; set them once at the top of main. Rounding is to nearest, so 2.0 / 3 at two decimals is 0.67, while an exact tie such as 0.125 rounds to even and prints 0.12 — never build an expectation on a tie.

Integers have their own modes: std::hex, std::oct and std::dec change the base and stay changed, std::showbase adds 0x, std::uppercase capitalises the digits. Forgetting to switch back to std::dec is a classic: every integer printed afterwards comes out in hex.

Sticky or one-shot

ManipulatorEffectLasts
std::setw(n)minimum widthnext insertion only
std::left / std::right / std::internalalignment within the widthuntil changed
std::setfill(c)padding characteruntil changed
std::fixed / std::scientific / std::defaultfloatfloating-point modeuntil changed
std::setprecision(n)digits (meaning depends on the mode)until changed
std::hex / std::oct / std::decinteger baseuntil changed
std::boolalphatrue/false instead of 1/0until changed

std::format

#include <format>

std::cout << std::format("{} scored {}\n", "Ada", 97);                        // Ada scored 97
std::cout << std::format("[{:>6}] [{:<6}] [{:^6}]\n", 42, 42, 42);            // [    42] [42    ] [  42  ]
std::cout << std::format("[{:6}] [{:6}]\n", 42, "ab");                        // [    42] [ab    ] - numbers right, text left
std::cout << std::format("{:05} {:+d} {:.3f} {:8.2f}\n", 42, 7, 3.14159, 2.5); // 00042 +7 3.142     2.50
std::cout << std::format("{:x} {:X} {:#x} {:08b} {:o}\n", 255, 255, 255, 5, 8); // ff FF 0xff 00000101 10
std::cout << std::format("{1} before {0}\n", "b", "a");                       // a before b
std::cout << std::format("{:*^11}\n", "mid");                                 // ****mid****

Each {} is replaced by the next argument; after a colon comes a specification in a fixed order: fill and align (< left, > right, ^ centre, optionally preceded by the fill character), sign (+), # for a base prefix, 0 for zero padding, width, .precision, and a type letter — d, x, X, b, o for integers, f, e, g for floating point, s for strings. Everything is optional; {:8.2f} reads "width 8, two decimals, fixed". Unlike streams, nothing is sticky: each placeholder says everything about itself, and the default alignment differs by kind — numbers right, text left, which is exactly what a table wants.

The format string must be known at compile time, because the compiler checks the placeholders against the argument types: {:d} with a std::string is a compile error, not a runtime surprise. For a string built at run time use std::vformat(fmt, std::make_format_args(args...)). std::format returns a std::string; std::print (C++23) writes straight to stdout but is not available on this runtime — write std::cout << std::format(...).

Building a string first

#include <sstream>

std::ostringstream out;
out << std::fixed << std::setprecision(1);
for (int i = 1; i <= 3; ++i) out << i << ':' << i * 0.5 << ' ';
std::string text = out.str();       // "1:0.5 2:1.0 3:1.5 "
std::cout << text << '\n';

An std::ostringstream accepts everything std::cout does and hands the result back with str(): assemble a line before deciding whether to print it, measure a string's width before padding, or collect a large output and write it once. std::format covers the simple cases since it already returns a string; the stream wins when many pieces with shared settings accumulate in a loop.

A table, done right

struct Row { std::string name; int qty; double price; };

void print_table(const std::vector<Row>& rows) {
    std::cout << std::format("{:<10}{:>5}{:>10}\n", "item", "qty", "price");
    for (const Row& r : rows) {
        std::cout << std::format("{:<10}{:>5}{:>10.2f}\n", r.name, r.qty, r.price);
    }
}
item        qty     price
pen           3      1.20
notebook     12     45.00

The rules that keep a table clean and a judge happy: right-align numbers so decimal points line up; put the left-aligned text column first so no line ends in padding; give each column a width that fits its widest value and use the same widths for the header. This judge ignores trailing spaces, but a left-aligned last column still produces them and other judges compare bytes — end each row with a right-aligned column and the problem never arises.

Pitfalls

  • setw is one-shot; setprecision, fixed, left, setfill and hex are sticky and stay on for the rest of the program.
  • setprecision(2) without fixed means two significant digits: 1234.5 prints as 1.2e+03.
  • std::hex left on turns every later integer into hex; switch back with std::dec.
  • The std::format string must be a compile-time constant; a std::string variable does not compile — use std::vformat.
  • A left-aligned last column pads with spaces to the end of the line; put text columns first.
  • Ties such as 0.125 round to even at two decimals; do not build an expectation on one.

Key takeaways

  • Streams: setw applies once; alignment, fill, precision, floating mode and base persist.
  • std::fixed plus setprecision(n) is the deterministic way to print n decimals; without fixed, n counts significant digits.
  • std::format("{:>8.2f}") reads fill/align, sign, #, 0, width, precision, type — nothing is sticky, numbers right-align and text left-aligns by default.
  • The format string is checked at compile time; it must be a literal.
  • std::ostringstream collects output into a string via str(); std::format returns one directly.
  • Put text columns first and right-align numbers so no line ends in padding.

Common questions

What does setw do in C++?

std::setw(n), from <iomanip>, sets the minimum width of the next value inserted into a stream, padding it with the fill character; the value after it is back to width 0. A value wider than the field is never truncated. Alignment comes from std::left or std::right, which stay in force.

What is the difference between setprecision with and without fixed?

Without std::fixed, std::setprecision(n) counts significant digits and may switch to scientific notation, so 1234.5 at precision 2 prints 1.2e+03. With std::fixed it counts digits after the decimal point, so std::fixed << std::setprecision(2) prints exactly two decimals. Both settings are sticky.

How does std::format work in C++?

std::format (C++20, <format>) replaces each {} in a compile-time format string with the next argument and returns a std::string. After a colon a placeholder can set fill and alignment, sign, width, precision and type: {:>8.2f} is right-aligned, width 8, two decimals. Nothing is sticky, and mismatched types are compile errors.

Which iomanip manipulators are sticky?

Only std::setw is one-shot, applying to the next insertion alone. std::left, std::right, std::setfill, std::fixed, std::scientific, std::setprecision, std::hex, std::dec and std::boolalpha stay in force until changed — so a forgotten std::hex prints every later integer in hexadecimal.

How do you print an aligned table in C++?

Give each column a fixed width that fits its widest value, left-align text and right-align numbers so decimal points line up — for example std::format("{:<10}{:>5}{:>10.2f}", name, qty, price). Put text columns first and end each row with a right-aligned column, so no line ends in padding.

Exercises

Receipt

Read an integer N and then N lines, each name qty price — a one-word item name, an integer quantity and a decimal unit price. Print one line per item and a total line, with exact widths:

  • item lines: name left-aligned in width 12, quantity right-aligned in width 4, line total (qty × price) right-aligned in width 10 with two decimals — the std::format spec is {:<12}{:>4}{:>10.2f};
  • the last line: TOTAL left-aligned in width 16, then the grand total right-aligned in width 10 with two decimals.

A name longer than 12 characters simply pushes its columns right. N may be 0, in which case only the total line is printed.

Input: N, then N lines. Output: N + 1 lines.

Example: input

3
pen 3 1.25
notebook 2 4.5
stapler 1 12.99

→ output

pen            3      3.75
notebook       2      9.00
stapler        1     12.99
TOTAL                25.74

Number bases

Read integers in the range 0 to 65535 until end of input (whitespace-separated). Print a header line and then one row per number showing it in four bases, columns separated by |:

  • decimal right-aligned in width 5;
  • hexadecimal in lower case, zero-padded to 4 digits;
  • octal right-aligned in width 6;
  • binary zero-padded to 16 digits.

The header uses the same widths, right-aligned: {:>5}|{:>4}|{:>6}|{:>16} with the words dec, hex, oct, bin; each row is {:>5}|{:04x}|{:>6o}|{:016b}. std::format makes the binary column trivial — with <iomanip> you would have to build it by hand. With no numbers at all, only the header is printed.

Input: zero or more integers. Output: the header, then one line per integer.

Example: input 255 0 65535 → output

  dec| hex|   oct|             bin
  255|00ff|   377|0000000011111111
    0|0000|     0|0000000000000000
65535|ffff|177777|1111111111111111

In this module: Strings

← Parsing input · std::string_view — a window onto text →