Number Bases in Programming
The stream does not know the base of a number when the number is written in the source code.
Three Bases from One Line
Suppose you want the same integer to appear on screen in a different number base. Ordinary cout output writes integers in decimal, which is the default, but C++ lets you change that choice while the output is being written. The tool for making that change is called a manipulator. In this article you will start with a single line of code that prints the same integer three times, and use it to see how the output is built one piece at a time.
What do you think happens?
Before reading on, look at a line that prints 1234 three times with the words hex and oct between the numbers. What do you expect the three copies of 1234 to look like on screen?
Reveal answer
The output is 1234 4d2 2322, with the first copy in decimal, the second in hexadecimal, and the third in octal.
The words hex and oct sit between the numbers, and they are the only things that change between the three copies. The value 1234 is the same each time; only the base the stream uses for writing it differs.
How the Output Stream Holds a Base Setting
The output stream carries a current setting for how integers should be written, and the simple manipulators change that setting. The stream does not know the base of a number when the number is written in the source code. It only knows the setting that is in effect when the number reaches it. This is why position matters: a manipulator placed before a number affects that number, and a manipulator placed after a number affects only the numbers that come later in the same statement.
A manipulator affects only the values that follow it, so its position in the statement determines the output.
The simple manipulators hex and oct change how the output stream writes integers, while decimal is the default form.
Tracing the Single Line Step by Step
The statement is evaluated from left to right, so the output is built in order. Each << sends the next item to the stream, and each manipulator is handled at the point where it appears in that sequence. The value 1234 never changes. Only the way the stream writes it changes.
1234 4d2 2322- The first 1234 is written before any base manipulator, so the stream uses its default, decimal.
- hex appears before the second 1234, so that copy is written in hexadecimal as 4d2.
- oct appears before the third 1234, so that copy is written in octal as 2322.
- The value 1234 is identical in all three positions; only the stream setting differs.
Reading the stream state left to right
Track the base setting as each piece of the line in the code above reaches the stream.
- 1First 1234
No base manipulator has been applied yet, so the stream is in its default decimal setting. The output is 1234.
- 2hex manipulator
hex changes the setting to hexadecimal. It writes nothing by itself, and it affects only what follows.
- 3Second 1234
The stream is now in hexadecimal, so 1234 is written as 4d2.
- 4oct manipulator
oct changes the setting from hexadecimal to octal for the values that come after it.
- 5Third 1234
The stream is now in octal, so 1234 is written as 2322.
The line prints 1234 4d2 2322. Each copy of the integer was the same value, and only the setting in effect when it reached the stream changed.
Decimal, Hexadecimal, and Octal Forms
The source identifies three ways an integer can be written with these manipulators. Decimal is the default, so a number printed with no base manipulator appears in decimal. Hexadecimal is selected with hex, and octal is selected with oct. The same integer value can be written in all three forms, as the line above shows with 1234.
| Manipulator | Base selected | How 1234 is written |
|---|---|---|
| none (default) | decimal | 1234 |
| hex | hexadecimal | 4d2 |
| oct | octal | 2322 |
One stored integer value, three written forms
Where the Manipulators Are Declared
The simple formatting manipulators are declared in the ios, istream, and ostream headers. The iomanip header is for manipulators that take arguments. Manipulators such as setw, which needs a width value, belong to that header. When you read code that uses hex, dec, or oct with no argument, the relevant declarations come from the stream headers rather than from iomanip.
Sticky Settings Across Statements
A base setting does not vanish when the statement ends. It belongs to the stream, so a hex or oct applied in one output statement can still be in effect for later output. The following program shows the setting carrying over from one line to the next. The second line contains no manipulator at all, yet it does not print in decimal.
ff
10
16- hex is applied before 255, so 255 is written as ff.
- The second statement has no manipulator, but the stream still holds hexadecimal, so 16 is written as 10.
- dec restores the default base before the last 16, which is then written in decimal.
Mistakes That Produce Unexpected Bases
Common mistake Assuming hex applies to the whole statement
What it looks likecout << 1234 << hex << 1234;Why it failsA manipulator affects only the values that follow it, so the first 1234 is still written in decimal.
Do this insteadPlace the manipulator before the first value that should use it, or accept that earlier values keep the previous setting.
Common mistake Expecting a fresh default in the next statement
What it looks likecout << hex << 255 << endl; cout << 16 << endl;
Why it failsThe base setting belongs to the stream and persists, so the second line prints 10 rather than 16.
Do this insteadApply dec explicitly when you want decimal again, as in cout << dec << 16.
Common mistake Looking in iomanip for every formatting manipulator
Why it failsThe simple manipulators are declared in the ios, istream, and ostream headers. iomanip is for manipulators that take arguments.
Do this insteadCheck the stream headers for hex, dec, and oct, and reserve iomanip for argument-taking manipulators such as setw.
Common mistake Reading the number in the source code as if the stream already knows its base
When an integer looks wrong, debugging should begin with the stream rather than the number. Check which base manipulator was in effect just before that value, and whether a manipulator appeared earlier than you expected. The divergence point is the position in the statement where the base setting differs from what you intended, not the number itself.
Predicting the Output of a Short Trace
Consider these two statements in order: cout << oct << 8 << endl; then cout << 9 << endl; Predict what each line prints, tracking the base setting after every piece reaches the stream.
Hints
- Ask first whether oct is in effect before the value 8 is written.
- Remember that the setting persists across statements, so the second line starts in octal unless something changed it.
- Octal 8 is written as 10, and octal 9 is written as 11.
Key Takeaways
- The same integer value, such as 1234, can be written as 1234 in decimal, 4d2 in hexadecimal, or 2322 in octal, depending on the base setting in effect when it reaches the output stream.
- Decimal is the default base. hex and oct change the stream's setting, and dec restores decimal.
- A manipulator affects only the values that follow it, so its position in the statement determines the output.
- The base setting persists across later output statements, so an unexpected format can be traced back to the manipulator that set it.
- The simple manipulators are declared in the ios, istream, and ostream headers, while iomanip is reserved for manipulators that take arguments.