Working with Number Bases in C++: Decimal, Octal, Hexadecimal, and Binary Conversion
C++ iostreams read and write integers in decimal by default. You can switch the base for formatted input and output using manipulators:
- std::dec for decimal
- std::oct for octal
- std::hex for hexadecimal
There is no built-in binary manipulator for streams; printing binary typically requires a helper function or std::bitset.
Reading and Writing in Different Bases
The base you set on a stream persists until you change it again. The following example reads four integers using diffferent bases and then prints them in various bases. It also demonstrates std::showbase and std::setw.
#include <iostream>
#include <iomanip>
int main() {
using std::cin;
using std::cout;
using std::endl;
long long o{}, x1{}, x2{}, d{};
cout << "Enter four integers in order: octal (o), hex (x1), hex (x2), decimal (d)\n";
// Input base is set with manipulators and persists until changed
cin >> std::oct >> o; // read octal
cin >> std::hex >> x1; // read hexadecimal
cin >> x2; // still hexadecimal
cin >> std::dec >> d; // back to decimal
// Show prefixes (0 for octal, 0x for hex) on output
cout << std::showbase;
cout << std::setw(12) << "o as hex:" << ' ' << std::hex << o << endl;
cout << std::setw(12) << "x1 as dec:" << ' ' << std::dec << x1 << '\t'
<< std::setw(12) << "x2 as dec:" << ' ' << x2 << endl;
cout << std::setw(12) << "d as oct:" << ' ' << std::oct << d << endl;
// Restore defaults
cout << std::dec << std::noshowbase;
return 0;
}
Notes
- For input, the stream base must be set on std::cin to interpret digits correctly. If the base is left as decimal, prefixes like 0 and 0x are not treated specially. If you want automatic base detection for prefixes, use std::setbase(0).
- Base manipulators only apply to formatted extraction/insersion of integer types, not floating-point or character types.
- The format, number, and types of inputs must match the variables you extract to, in order, or later extractions will fail.
- Once you set a base on cin or cout, it stays in effect until you set a different one.
Printing Binary Representations
There is no standard stream manipulator for binary. Below are four common techniques to produce a binary string or print bits for unsigned integers.
#include <iostream>
#include <vector>
#include <string>
#include <bitset>
#include <limits>
#include <iomanip>
// 1) Recursive printing: most-significant bit first
void print_binary_recursive(unsigned int v) {
if (v >= 2) {
print_binary_recursive(v >> 1);
}
std::cout << (v & 1u);
}
// 2) Build a string via container (LSB to MSB, then reverse)
std::string to_binary_string(unsigned int v) {
if (v == 0) return "0";
std::vector<char> buf;
while (v != 0) {
buf.push_back(static_cast<char>('0' + (v & 1u)));
v >>= 1;
}
return std::string(buf.rbegin(), buf.rend());
}
// 3) Fixed-width scan using bit operations
template <class Unsigned>
void print_binary_scan(Unsigned v) {
static_assert(std::numeric_limits<Unsigned>::is_integer && !std::numeric_limits<Unsigned>::is_signed,
"Unsigned integer required");
for (int i = std::numeric_limits<Unsigned>::digits - 1; i >= 0; --i) {
std::cout << ((v >> i) & 1u);
}
std::cout << '\n';
}
// 4) Use std::bitset for a fixed-width representation
void print_binary_bitset(unsigned int v) {
std::cout << std::bitset<sizeof(unsigned int) * 8>(v) << '\n';
}
int main() {
unsigned int lhs = 1045;
unsigned int rhs = 2;
unsigned int sum = lhs + rhs;
std::cout << std::setw(22) << "recursive(" << sum << "): ";
print_binary_recursive(sum);
std::cout << '\n';
std::cout << std::setw(22) << "container(" << sum << "): "
<< to_binary_string(sum) << '\n';
std::cout << std::setw(22) << "scan(" << sum << "): ";
print_binary_scan(sum);
std::cout << std::setw(22) << "bitset(" << sum << "): ";
print_binary_bitset(sum);
return 0;
}
Additional details
- The recursive method prints the most-significant bit first by recursing untill the high end is reached, then unwinding to emit lower bits. Every recursive call pushes a new stack frame, which includes argumants, local variables, and a return address; deep recursion increases stack usage.
- The container approach builds the binary representation from least-significant to most-significant bit and then reverses it for output.
- The scan method emits a fixed number of bits (width of the type). Use an unsigned type to avoid implementation-defined shifts on signed integers.
- std::bitset<N> offers a concise, fixed-width binary view without menual loops.
Formatting tip
- Use std::setw(n) from to set the field width for the next output item only. For example, std::cout << std::setw(8) << value; aligns the value within an 8-character field.