Output Formatting in C++
A sticky manipulator persists for subsequent operations on the stream rather than applying only to one output statement.
A Setting You Change Once and Forget
Output formatting in C++ often depends on a setting that you change once and then forget about. A single formatting call can change how every later floating-point number is printed, even when the output statements that follow it never mention formatting at all. This is the core trap this article is about, and the fastest way to understand it is to predict the output of a short program before reading the explanation.
- Two floating-point values and one integer are printed twice, in two groups of three lines.
- The call between the two groups is the only formatting change in the program.
What do you think happens?
Before reading on, decide what each of the six lines will print. Which lines, if any, will look different from the first group?
Reveal answer
The first group prints 1234.5679, 98.765432, and 123456. The second group prints 1235, 98.77, and 123456.
The precision call sets how many significant digits floating-point values use. The first call sets it to 8, and the second call sets it to 4. Nothing resets it between the two groups, so each group uses the setting that was last applied. The integer 123456 is not a floating-point value, so it prints the same way both times.
Tracing the Precision Value Line by Line
The easiest way to follow a sticky setting is to track its value at each point in the program. Start at the top of the code and write down the active precision after every statement that might change it. Output statements do not change the setting. Only the precision call does, so the value stays constant between calls and changes only when the program makes a new call.
Read the diagram from left to right. The first printed floating-point value, 1234.5679, appears while the setting is 8. The second group begins after the precision call that sets the value to 4, so the value 1235 is the first line of output that differs from the first group. The line for 123456 is identical in both groups because it is an integer, and the setting does not apply to it. The divergence is therefore not caused by the output statement that prints 1235. It is caused by the most recent precision call before that statement.
What Makes a Manipulator Sticky
A sticky manipulator persists: once you apply it to a stream, its effect stays in place for subsequent operations rather than applying only to the single output statement it appears in. The precision setting is the standard example of a sticky setting, and it affects subsequent floating-point output operations.
The call that sets precision is cout.precision(n), where n is the value you pass in. In the program above, the first call sets precision to 8 before any output happens, so the first pair of floating-point values is printed with that setting. The two output statements that follow do not set precision again, so the setting stays in force. The program does not need to re-apply the setting before each output statement, because the stream remembers it. The setting remains until the program calls precision again.
Think of cout as an object that holds a current configuration. Every output statement sent to cout reads that configuration and may print using it. A precision call changes the stored configuration, not just the next line of output. This is why the setting appears to affect output that looks unrelated to the call. The statements that print 1235 and 98.77 contain no formatting instructions of their own. They simply inherit the most recent setting.
Reading Where the Output Diverges
When printed output does not match what you expected, avoid guessing. Work through the program in order and do two checks for each printed line. First, find the most recent precision call before that statement. Second, check whether the value being printed is floating-point. If the value is an integer, the precision setting cannot explain a difference in that line. If the value is floating-point, the most recent precision call determines how many digits appear.
Locating a surprising line
A program prints the value 98.7654321 and then prints it again after a precision call. The second line shows 98.77, and the programmer expected 98.765432 because they only remember the first call. Where does the output diverge, and why?
- 1Find the most recent precision call
The program calls
precision(4)before the second print. That call is the most recent one before the line in question, so it is the active setting for that line. - 2Check the type of the printed value
98.7654321 is a floating-point value, so the precision setting applies to it.
- 3Apply the active setting
With precision 4, the value is printed with 4 significant digits, which gives 98.77. The earlier value of 8 has been replaced and no longer applies.
- 4Identify the divergence point
The divergence occurs at the second print, because that is the first line printed after the
precision(4)call. The earlier line is correct for precision 8.
The output diverges at the line printed after the precision(4) call. The cause is the sticky setting from that call, not an error in the print statement itself.
1234.5679
98.765432
123456
1235
98.77
123456Mistakes Beginners Make with Sticky Settings
Common mistake Assuming a precision call affects only the statement it sits on
What it looks likeCalling cout.precision(4) and then expecting the next cout line to print at the default precision.
Why it failsA sticky manipulator persists for subsequent operations on the stream, so the setting applies to later floating-point output as well.
Do this insteadAfter each precision call, assume it stays in effect until the next precision call, and trace it forward through the code.
Common mistake Expecting the setting to change integers
What it looks likePrinting 123456 after a precision call and expecting it to shorten or round.
Why it failsThe sticky precision setting affects floating-point output. An integer such as 123456 prints unchanged.
Do this insteadWhen checking output, first determine whether each value is floating-point before attributing a change to the precision setting.
Common mistake Re-reading the output statement instead of the precision history
What it looks likeLooking only at the cout line that printed 1235 and not noticing the precision(4) call above it.
Why it failsThe output statement contains no formatting instruction. Its result depends on the stream's current state.
Do this insteadTrace backward to the most recent precision call before the surprising line.
Common mistake Forgetting that a second precision call replaces the first rather than adding to it
Practice Predicting Stream Output
Suppose a program sets precision to 6, prints a floating-point value, prints an integer, then sets precision to 3 and prints the same floating-point value again. Without running the program, write down the active precision at each printed line, and identify the first line whose output differs from the line before the precision(3) call.
Hints
- Start with the first precision call and mark it as the active setting for every line that follows until the next call.
- The integer line does not change, regardless of which precision is active.
- The first line that uses the new setting is the first line printed after the
precision(3)call.
Explain, in one or two sentences, why a program that sets precision once at the top and never sets it again still prints every later floating-point value with that precision.
Hints
- Think about what a sticky manipulator means for the stream.
Key Takeaways
- A sticky manipulator persists on the stream, so
cout.precision(n)affects every later floating-point output until the program calls precision again. - Tracing the most recent precision call before each printed line is the reliable way to locate where output diverges from expectations.
- The sticky precision setting affects floating-point values; integers such as 123456 print the same regardless of the setting.
- When output looks wrong, check the value's type and the active precision before suspecting the output statement itself.