iostream formatting

The stream operators of lesson 004 write values in a default format. The manipulators change that format: width, fill, precision, base and the words for bool. They are objects you insert into the stream like a value, and they change the stream, not the expression.

The formatting library of lesson 063 does the same job per value and leaves nothing behind. Streams keep their state, which is convenient for a table and a source of surprises in the next function that writes to the same stream.

Every one of these settings belongs to the stream, not to the value, which is why a function that changes one should say so or restore it. A table printed with the manipulators is the classic case where the state is worth setting once and resetting at the end.

The stream as a chain

std::cout << value returns the stream, which is why the calls chain. The type of each argument decides how it is written: an int as digits, a double with six significant digits, a char as a character.

The program writes a line in one chain and then a second with std::flush in the middle. flush pushes the buffer to the operating system without ending the line, which matters when a program waits for input or writes to a pipe.

std::endl is a newline followed by a flush; '\n' is only the newline. In a loop that writes thousands of lines, '\n' is faster because it does not flush each one.

#include <iostream> int main() { int count = 3; double price = 2.5; std::cout << "count " << count << ", price " << price << '\n'; std::cout << "before" << std::flush << " after" << '\n'; std::cout << "third line" << std::endl; }

g++ -std=c++23 -Wall -Wextra chain.cpp -o chain
./chain

count 3, price 2.5
before after
third line

Width, fill and alignment

std::setw sets the width of the next output only. The default alignment is right, std::left and std::right change it, and std::setfill chooses the padding character.

The program prints the same word three times in six columns: right-aligned by default, left-aligned with std::left, and padded with asterisks. The brackets show where the field begins and ends.

Because setw applies to one value, a table needs it before every field, while std::left and std::setfill stay set until they are changed. That difference is the most common reason a table loses its alignment halfway down.

#include <iomanip> #include <iostream> int main() { std::cout << '[' << std::setw(6) << "id" << ']' << '\n'; std::cout << '[' << std::left << std::setw(6) << "id" << ']' << '\n'; std::cout << '[' << std::setfill('*') << std::setw(6) << 42 << ']' << '\n'; }

g++ -std=c++23 -Wall -Wextra width.cpp -o width
./width

[ id]
[id ]
[42****]

Precision for floating point

The default format of a double prints six significant digits. std::fixed switches to a fixed number of digits after the point, std::setprecision sets how many, and std::scientific uses the exponent form.

The program prints the same value four ways: default, fixed with three decimals, scientific and back to the default with std::defaultfloat. The last line is the reminder that the settings persist.

std::setprecision means digits after the point in fixed and scientific mode, and significant digits in the default mode. The two meanings share one function, which is why a program that changes mode should set the precision again.

#include <iomanip> #include <iostream> int main() { double value = 2.0 / 3.0; std::cout << value << '\n'; std::cout << std::fixed << std::setprecision(3) << value << '\n'; std::cout << std::scientific << value << '\n'; std::cout << std::defaultfloat << value << '\n'; }

g++ -std=c++23 -Wall -Wextra precision.cpp -o precision
./precision

0.666667
0.667
6.667e-01
0.667

Bases and booleans

std::hex, std::oct and std::dec choose the base of every integer written afterwards. std::showbase adds the 0x or 0 prefix, and std::boolalpha writes true and false instead of 1 and 0.

The program prints 255 in three bases, then 255 in hexadecimal with the prefix and in decimal again. The two lines show that the base is a stream setting, not an argument.

Every one of these settings stays on the stream. A function that writes a number in hex and returns leaves std::cout in hex for its caller, which is why the stream state is worth resetting at the end of such a function.

#include <iomanip> #include <iostream> int main() { std::cout << std::hex << 255 << ' ' << std::oct << 255 << ' ' << std::dec << 255 << '\n'; std::cout << std::showbase << std::hex << 255 << std::dec << ' '; std::cout << std::boolalpha << true << ' ' << false << '\n'; }

g++ -std=c++23 -Wall -Wextra base.cpp -o base
./base

ff 377 255
0xff true false

A manipulator without its header

The manipulators with an argument, such as setw, setfill and setprecision, live in <iomanip>, not in <iostream>. Using one without the header is an unknown name, and the compiler says it is not a member of std.

The program includes only <iostream> and calls std::setprecision. The error names the function and the namespace, and the fix is the missing include, which the compiler cannot suggest because the name is simply absent.

The parameterless manipulators, such as std::hex, std::left and std::boolalpha, are in <ios> and come with <iostream>. That split is why half of a program can compile and the other half cannot.

#include <iostream> int main() { std::cout << std::setprecision(2) << 3.14159 << '\n'; }

g++ -std=c++23 -Wall -Wextra no_iomanip.cpp -o no_iomanip

no_iomanip.cpp: In function 'int main()':
no_iomanip.cpp:5:23: error: 'setprecision' is not a member of 'std'
5 | std::cout << std::setprecision(2) << 3.14159 << '\n';
| ^~~~~~~~~~~~
no_iomanip.cpp:2:1: note: 'std::setprecision' is defined in header '<iomanip>'; this is probably fixable by adding '#include <iomanip>'
1 | #include <iostream>
+++ |+#include <iomanip>
2 |

Exercises

  1. Print a table of three rows with a name in ten columns and a price in eight, aligned on the decimal point with fixed and setprecision.
  2. Print the numbers 1 to 5 in decimal, hexadecimal and octal, one base per line.
  3. Write a function that prints one number in hex and restores the stream to decimal before it returns.

Questions

A new set of questions in a new order every time. Choose one answer for each, then check.

  1. What does inserting std::endl do?

    Answer

    It ends the line and flushes the stream

    The manipulator writes the newline and then flushes.

  2. Where does std::setw apply?

    Answer

    To the next output only

    The width is reset after one insertion, unlike most other manipulators.

  3. What does std::fixed change?

    Answer

    The format of floating point output to fixed notation

    The floatfield is set to fixed, and setprecision then means digits after the point.

  4. What does std::boolalpha do?

    Answer

    It prints bool values as true and false

    The flag changes the text of the two bool values.

  5. What does this program print? #include <iostream> int main() { std::cout << "count " << 3 << ", price " << 2.5 << '\n'; std::cout << "before " << "after" << '\n'; std::cout << "third" << " line"; }

    Answer

    count 3, price 2.5 / before after / third line

    The chain writes the pieces in order, and 2.5 keeps its short default form.

  6. What does this program print? #include <iomanip> #include <iostream> int main() { std::cout << '[' << std::setw(6) << "id" << "]\n"; std::cout << '[' << std::left << std::setw(6) << "id" << "]\n"; std::cout << '[' << std::right << std::setfill('*') << std::setw(6) << 42 << "]"; }

    Answer

    [ id] / [id ] / [****42]

    The first line is right-aligned by default, the second left-aligned, and the fill pads the number.

  7. What does this program print? #include <iomanip> #include <iostream> int main() { std::cout << 2.0 / 3.0 << '\n'; std::cout << std::fixed << std::setprecision(3) << 2.0 / 3.0 << '\n'; std::cout << std::scientific << std::setprecision(3) << 2.0 / 3.0 << '\n'; std::cout << std::defaultfloat << std::setprecision(3) << 2.0 / 3.0; }

    Answer

    0.666667 / 0.667 / 6.667e-01 / 0.667

    The default, fixed, scientific and default-float formats each print the value in their own shape.

  8. What does this program print? #include <iomanip> #include <iostream> int main() { std::cout << std::hex << 255 << ' ' << std::oct << 255 << ' ' << std::dec << 255 << '\n'; std::cout << std::showbase << std::hex << 255 << ' ' << std::boolalpha << (1 == 1) << ' ' << (1 == 2); }

    Answer

    ff 377 255 / 0xff true false

    The base manipulators print the first line, and showbase plus boolalpha print the second.

  9. What does the compiler report for this line? #include <iostream> int main() { std::cout << std::setprecision(2) << 3.14159 << '\n'; }

    Answer

    'setprecision' is not a member of 'std'

    The manipulator lives in <iomanip>, and the note names the missing header.

  10. Why prefer the format library over the manipulators for new code?

    Answer

    The format string is checked and the layout is local to the call

    The stream flags are global to the stream and easy to leave changed.

The code on this page was built with g++ 15.2.0 and run on Alpine Linux 3.24.3 (x86_64) in a Docker container. The programs whose section shows a compiler error were compiled until that error and were not run.

Article author: Arthur Isaev