Concepts / Number Bases in Programming

Number Bases in Programming

The stream does not know the base of a number when the number is written in the source code.

  • Programming

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
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.

Key point

A manipulator affects only the values that follow it, so its position in the statement determines the output.

Definition

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.

cpp
Output
1234 4d2 2322
Notes on the code
  • 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.
Worked example

Reading the stream state left to right

Track the base setting as each piece of the line in the code above reaches the stream.

  1. 1
    First 1234

    No base manipulator has been applied yet, so the stream is in its default decimal setting. The output is 1234.

  2. 2
    hex manipulator

    hex changes the setting to hexadecimal. It writes nothing by itself, and it affects only what follows.

  3. 3
    Second 1234

    The stream is now in hexadecimal, so 1234 is written as 4d2.

  4. 4
    oct manipulator

    oct changes the setting from hexadecimal to octal for the values that come after it.

  5. 5
    Third 1234

    The stream is now in octal, so 1234 is written as 2322.

Result

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.

ManipulatorBase selectedHow 1234 is written
none (default)decimal1234
hexhexadecimal4d2
octoctal2322

One stored integer value, three written forms

decimaldefault settinghexadecimalafter hexoctalafter octhexoctdec
Which base is in effect for the next integer after each manipulator is applied to cout

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.

cpp
Output
ff
10
16
Notes on the code
  • 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.
hex then 255ff16 with nomanipulator10dec then 1616hex still in effectdec restores default
The base in effect for each integer as the program runs from top to bottom

Mistakes That Produce Unexpected Bases

  • Common mistake

    Assuming hex applies to the whole statement

    What it looks like
    cout << 1234 << hex << 1234;
    Why it fails

    A manipulator affects only the values that follow it, so the first 1234 is still written in decimal.

    Do this instead

    Place 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 like

    cout << hex << 255 << endl; cout << 16 << endl;

    Why it fails

    The base setting belongs to the stream and persists, so the second line prints 10 rather than 16.

    Do this instead

    Apply dec explicitly when you want decimal again, as in cout << dec << 16.

  • Common mistake

    Looking in iomanip for every formatting manipulator

    Why it fails

    The simple manipulators are declared in the ios, istream, and ostream headers. iomanip is for manipulators that take arguments.

    Do this instead

    Check 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

MEDIUM

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.

Related concepts

More concepts starting with N