Stream Manipulators and Formatting
The program below prints an itemized receipt to the terminal:
If you look at the output, you’ll notice how the columns don’t align properly:
Item Qty Price
Coffee 2 3.5
Espresso 1 2.75
Water 3 1The prices are also not formatted consistently, with 1.0 printing out as 1, instead of 1.00.
C++ gives us a set of tools called stream manipulators that control exactly how values appear when printed. Most of them come from the <iomanip> header, so we’ll include it alongside <iostream> in nearly every program that does output formatting:
#include <iostream>#include <iomanip>Let’s fix the output above, one problem at a time.
Problem 1: prices don’t show two decimal places
By default, std::cout prints floating-point numbers with up to six significant digits, dropping trailing zeros. That’s why 1.0 becomes 1 and 3.5 stays 3.5 instead of 3.50.
Two manipulators fix this together. std::fixed tells the stream to always show a decimal point and a consistent number of decimal places. By default that’s six decimal places, but std::setprecision lets you change that number.
Here we set it to two:
Item Qty Price
Coffee 2 3.50
Espresso 1 2.75
Water 3 1.00The prices now appear in the desired format. Notice that those manipulators are only set once, on line 5. They keep applying to every number printed after that point, for the rest of the program, unless you explicitly change them. This is called being sticky. Most manipulators work this way.
Problem 2: the columns don’t line up
The real issue is that each value takes up only as much space as it needs. "Coffee" is six characters, "Espresso" is eight, so the rest of the row shifts. To fix this, you need each value to occupy a fixed amount of space regardless of how wide it actually is.
std::setw(n) reserves n characters of width for the next value to be printed. If the value is shorter, the output is padded with spaces. If the value is wider than n, setw has no effect and the full value prints as-is.
Item Qty Price
Coffee 2 3.50
Espresso 1 2.75
Water 3 1.00Notice that setw is applied repeatedly for each output. That’s because it’s not sticky and resets after each use. Every other manipulator in this lesson is sticky, setw being the only exception.
Problem 3: the item names should be left-aligned
Numbers and text can be aligned either way, but convention aligns text labels to the left and numbers to the right. Right now everything is right-aligned, which is the default.
std::left switches to left-alignment. std::right switches back. Both are sticky.
Item Qty Price
Coffee 2 3.50
Espresso 1 2.75
Water 3 1.00The alignment manipulator must appear before setw, which in turn appears before the value to which it applies.
Problem 4: adding a divider line
A horizontal line between the header and the rows would make this look like a real table. You could print a string of dashes manually, but there’s an easier way.
The default padding character used by setw is a space. std::setfill(c) changes it to any character c you choose. For example, setting the fill character to '-' and printing an empty string with a set width gives you a full row of dashes:
std::cout << std::setfill('-') << std::setw(26) << "" << '\n';std::cout << std::setfill(' '); // Reset to spaces immediately afterThe reset to spaces afterward is important. If you forget it, everything you print next will be padded with dashes.
The finished receipt
Here’s the complete version, now with a total row:
Item Qty Price Total
----------------------------------
Coffee 2 3.50 7.00
Espresso 1 2.75 2.75
Water 3 1.00 3.00
----------------------------------
Total 12.75Showing the sign explicitly
On some receipts you might want to show a + or - against each quantity, to distinguish items added from items removed due to an error, for example. By default, C++ only prints the minus sign on negative numbers. std::showpos forces the + sign to appear on positive ones too.
Like the others, it’s sticky, so use std::noshowpos to turn it off.
Saving and restoring formatting state
If something is printing in the wrong format and you can’t figure out why, a sticky manipulator set somewhere earlier is almost always the cause.
A more disciplined approach is to save the formatting state before you change it, and restore it when you’re done: