Literals and Type Conversion

Binary, Octal, and Hexadecimal Literals

This lets you write integer literals in some other base than ten using binary, octal, or hexadecimal literals. The compiler stores the same bits in memory; you are just changing the notation used to represent those bits in the source.

If you work with bit patterns, memory locations, or hardware registers, this can be far easier to read than decimal.

A binary literal is introduced as 0b or 0B followed by a string of 0s and 1s. This feature is supported since C++14.

// Use binary notation for this integer literal
0b1010          // decimal 10
0b11111111      // decimal 255
0B10000000      // decimal 128

All digits are either 0 or 1. If you hit a 2 by mistake, then the code won't compile; it's a good strict check that stops typos.

Binary is the clearest way to represent individual bits. When you are setting flags, masking bits, or talking about a hardware control register, the precise pattern is often easier than trying to do the conversion in your head.

I prefer binary whenever the bit layout itself is the critical information.

But you can still affix the normal integer suffixes.

// Use the unsigned suffix on this binary integer literal
0b1010u         // unsigned int
0b1'0000'0000LL // long long

You may use digit separators (') anywhere in any integer literal and it will have no effect on the value; they merely make long strings more human readable.

An octal literal is prefixed with a zero, then followed by any number of digits 0 through 7.

// A leading zero makes this an octal literal
0755            // decimal 493
010             // decimal 8
00              // decimal 0

The only prefix is the leading zero. There is no 0o form in normal C++.

Since a single zero already serves as a prefix for an octal number, a casual keystroke can turn a value that you intended to be decimal into something entirely different. 010 when you expected it to be ten gives you eight, and the compiler happily compiles the code without the slightest noise, and it all goes wrong at runtime.

Octal is still visible in a few places, mostly in constants used for Unix style file permissions and in legacy code that interfaces with hardware. For anything new, binary or hex is more obvious.

If you're forced to use octal, don't add any 8 or 9s to the literal.

The suffixes operate the same way as usual with the other integer literals.

// The suffix makes this integer literal unsigned
0755u           // unsigned int
0755LL          // long long

A hexadecimal literal begins with 0x or 0X, followed by a sequence of valid hexadecimal digits that can be 0 to 9 or A to F in either case.

// Use hexadecimal notation for this integer literal
0xFF            // decimal 255
0x2A            // decimal 42
0X10            // decimal 16
0xDEADBEEF      // a common 32 bit pattern

Hex is tight. Four binary digits squeeze into just one hex character, so a 32 bit number needs only eight hexadecimal digits. That succinctness is why hex is standard for addresses, color values, and bit masks wider than a few bits.

The characters A to F can be upper or lower case; for example, 0xFF and 0xff are equivalent. Mixing case inside a literal is legal but unattractive, so most programmers choose one style and stick to it.

There are suffixes also.

0xFFu           // unsigned int
0xABCDEFULL     // unsigned long long

The floating point form with p should become familiar later; for normal integers the rules remain simple.

All three forms return normal integer values. The type is inferred by the same rules as for decimal literals: the value is an int if it fits, otherwise the next larger type that can contain it, unless a suffix forces a different type.

This doesn't change the type, only the spelling.

Since the compiler translates the literal to binary before the program executes, the selection of base is just for the human reading the program. Selecting the form matching the problem domain eliminates translation steps in the mind and minimizes the risk of misreading a constant at some later stage.

  • A leading zero on a decimal value causes it to be interpreted as octal. For instance, 010 is 8, not 10.

  • Digits outside the allowed set are errors in binary and octal. A typo in a binary or octal literal causes a compile error, but a different valid hex digit can give a wrong value while still compiling.

  • Very large literals without a suffix may not have the type you expect. Append LL or ULL to reveal the type you expect.

  • Digit separators are optional. They make long binary and hex constants more readable but are very easy to skip when copying constants from documentation.

Various binary, octal, and hexadecimal spellings can all represent the same number. A binary representation of ten, an octal representation of ten, and a hexadecimal representation of ten all compare equal because the base is just a different way to display the value.

#include <iostream>

int main()
{
    // Binary
    int bin1{0b1010};
    int bin2{0b11111111};
    unsigned int bin3{0b10000000u};

    // Octal
    int oct1{012};
    int oct2{0777};

    // int bad{078}; // error: digit 8 is not allowed in octal

    // Hexadecimal
    int hex1{0xA};
    int hex2{0xFF};
    unsigned long long hex3{0xDEADBEEFull};

    std::cout << "Binary 0b1010     = " << bin1 << '\n';
    std::cout << "Binary 0b11111111 = " << bin2 << '\n';
    std::cout << "Binary 0b10000000 = " << bin3 << '\n';
    std::cout << '\n';

    std::cout << "Octal  012        = " << oct1 << '\n';
    std::cout << "Octal  0777       = " << oct2 << '\n';
    std::cout << '\n';

    std::cout << "Hex    0xA        = " << hex1 << '\n';
    std::cout << "Hex    0xFF       = " << hex2 << '\n';
    std::cout << "Hex    0xDEADBEEF = " << hex3 << '\n';

    if (bin1 == oct1 && oct1 == hex1)
    {
        std::cout << "\nAll three forms of ten compare equal.\n";
    }
}

A leading zero can subtly alter the meaning of a number by indicating to the compiler that it should be interpreted as octal. Integer suffixes may be used if the signedness or type needs to be explicitly indicated.

Exercise: Binary, Octal, and Hexadecimal LiteralsStarter code, hints and solutionOpenClose
Guided practiceBeginner · 10–15 min

Practice: Binary, Octal, and Hexadecimal Literals

Work through the mission, reveal hints only when needed, and compare your result with the model solution.

Your challenge

Change bin1 from binary ten to binary eleven and update its output label. Predict the final equality condition against the octal and hexadecimal values.

Mission checklist

  1. Run the starter as a baseline, then predict the change requested in Your challenge.
  2. Make that change in your editor; use the lesson-specific hints if you need help.
  3. Compile and run your answer, then compare with the worked solution and explain the result.

Starter program

This is the working baseline, not the completed answer. Compile as C++20, then make the change described above.

#include <iostream>

int main()
{
    // Binary
    int bin1{0b1010};
    int bin2{0b11111111};
    unsigned int bin3{0b10000000u};

    // Octal
    int oct1{012};
    int oct2{0777};

    // int bad{078}; // error: digit 8 is not allowed in octal

    // Hexadecimal
    int hex1{0xA};
    int hex2{0xFF};
    unsigned long long hex3{0xDEADBEEFull};

    std::cout << "Binary 0b1010     = " << bin1 << '\n';
    std::cout << "Binary 0b11111111 = " << bin2 << '\n';
    std::cout << "Binary 0b10000000 = " << bin3 << '\n';
    std::cout << '\n';

    std::cout << "Octal  012        = " << oct1 << '\n';
    std::cout << "Octal  0777       = " << oct2 << '\n';
    std::cout << '\n';

    std::cout << "Hex    0xA        = " << hex1 << '\n';
    std::cout << "Hex    0xFF       = " << hex2 << '\n';
    std::cout << "Hex    0xDEADBEEF = " << hex3 << '\n';

    if (bin1 == oct1 && oct1 == hex1)
    {
        std::cout << "\nAll three forms of ten compare equal.\n";
    }
}

Hints

Hint 1

Decode 0b1010, 012, and 0xA using bases 2, 8, and 16. The leading zero in 012 matters: it is not decimal twelve.

Hint 2

Change only one of bin1, oct1, or hex1, then revisit both == comparisons joined by &&. The final message requires all three stored values to agree.

Worked solution

bin1 is changed from binary ten to binary eleven. oct1 and hex1 still represent ten, so the final equality condition is false. The label is updated to match the new literal rather than misdescribe the value.

#include <iostream>

int main()
{
    // Binary
    int bin1{0b1011};
    int bin2{0b11111111};
    unsigned int bin3{0b10000000u};

    // Octal
    int oct1{012};
    int oct2{0777};

    // int bad{078}; // error: digit 8 is not allowed in octal

    // Hexadecimal
    int hex1{0xA};
    int hex2{0xFF};
    unsigned long long hex3{0xDEADBEEFull};

    std::cout << "Binary 0b1011     = " << bin1 << '\n';
    std::cout << "Binary 0b11111111 = " << bin2 << '\n';
    std::cout << "Binary 0b10000000 = " << bin3 << '\n';
    std::cout << '\n';

    std::cout << "Octal  012        = " << oct1 << '\n';
    std::cout << "Octal  0777       = " << oct2 << '\n';
    std::cout << '\n';

    std::cout << "Hex    0xA        = " << hex1 << '\n';
    std::cout << "Hex    0xFF       = " << hex2 << '\n';
    std::cout << "Hex    0xDEADBEEF = " << hex3 << '\n';

    if (bin1 == oct1 && oct1 == hex1)
    {
        std::cout << "\nAll three forms of ten compare equal.\n";
    }
}

Example output

Binary 0b1011     = 11
Binary 0b11111111 = 255
Binary 0b10000000 = 128

Octal  012        = 10
Octal  0777       = 511

Hex    0xA        = 10
Hex    0xFF       = 255
Hex    0xDEADBEEF = 3735928559

0 comments

No comments yet. Be the first.

Add a comment

About CppLearn

CppLearn is an interactive, free reference for modern C++. In each lesson, you'll find out how the language works, rather than just lines of code that you can copy and paste. We cover concepts starting from beginner lessons on fundamentals and proceed to templates, move semantics, smart pointers, and areas that usually trap or scare C++ learners.