Number Systems Explained: Binary, Octal, Decimal, and Hex
When you write 0xFF in code, you're using hexadecimal. When you run chmod 755, you're using octal. When you look at memory addresses or bit flags, you're dealing with binary represented as hex. Understanding number bases isn't just academic — it unlocks large parts of systems programming, networking, and hardware interfacing.
Convert between bases instantly: Our Number Base Converter converts decimal, hex, binary, and octal — type in any field and all others update in real time.
Open Number Base Converter →Table of Contents
Why Computers Use Binary
At the hardware level, computers store information as electrical states: high voltage (1) or low voltage (0). There's no physical way to represent "3" or "7" in a single transistor — only on or off. Binary (base 2) maps directly to this physical reality: every bit is a single transistor, and a byte (8 bits) is eight transistors that can collectively represent values from 0 (00000000) to 255 (11111111).
All other number systems that computers use — hex, octal — are just convenient shorthand for humans to read and write binary more compactly.
How Each Base Works
In any number system, a "base" tells you how many unique digits exist and what each position represents. In base 10 (decimal), the digits are 0–9 and each position is a power of 10. In base 2, the digits are 0–1 and each position is a power of 2.
| Position | Base 10 value | Base 2 value | Base 16 value |
|---|---|---|---|
| Position 0 (rightmost) | 10⁰ = 1 | 2⁰ = 1 | 16⁰ = 1 |
| Position 1 | 10¹ = 10 | 2¹ = 2 | 16¹ = 16 |
| Position 2 | 10² = 100 | 2² = 4 | 16² = 256 |
| Position 3 | 10³ = 1000 | 2³ = 8 | 16³ = 4096 |
Binary (Base 2)
Binary uses only the digits 0 and 1. To read a binary number, multiply each digit by its positional power of 2 and sum the results:
Binary: 1 0 1 1 0 1
↓ ↓ ↓ ↓ ↓ ↓
2⁵=32 2⁴=16 2³=8 2²=4 2¹=2 2⁰=1
×1 ×0 ×1 ×1 ×0 ×1
32 + 0 + 8 + 4 + 0 + 1 = 45
So binary 101101 = decimal 45
Binary appears in: bitwise operations (&, |, ^, ~, <<, >>), boolean flags packed into an integer, network subnet masks, and low-level hardware registers.
Hexadecimal (Base 16)
Hex uses digits 0–9 and letters A–F (representing 10–15). One hex digit represents exactly 4 bits, which means one byte (8 bits) is always exactly two hex digits. This compact relationship with binary makes hex the universal shorthand for binary data.
// Converting hex to decimal:
0xFF = (15 × 16¹) + (15 × 16⁰) = 240 + 15 = 255
0x1A = (1 × 16¹) + (10 × 16⁰) = 16 + 10 = 26
// Converting between hex and binary (each hex digit = 4 bits):
0xA5 = 1010 0101
A=1010 5=0101
0xFF = 1111 1111
F=1111 F=1111
Hex appears in: memory addresses (0x7ffe5c24a0b0), CSS color codes (#FF5733), MAC addresses (00:1A:2B:3C:4D:5E), ASCII and Unicode code points (U+1F600), SHA-256 hashes, and cryptographic keys.
Octal (Base 8)
Octal uses digits 0–7. Each octal digit represents exactly 3 bits. Octal was more important in older computing systems and is still used today for Unix file permissions.
// Unix file permissions: chmod 755
// 7 = 111 binary = rwx (read, write, execute) for owner
// 5 = 101 binary = r-x (read, execute, no write) for group
// 5 = 101 binary = r-x (read, execute, no write) for others
// 644 = rw-r--r-- (common for files)
// 755 = rwxr-xr-x (common for executables and directories)
// In Python, prefix octal with 0o:
os.chmod("file.txt", 0o755)
Language Prefixes
| Base | C/C++/Java/JS | Python | Example |
|---|---|---|---|
| Binary | 0b | 0b | 0b1010 = 10 |
| Octal | 0 or 0o | 0o | 0o17 = 15 |
| Decimal | (none) | (none) | 255 |
| Hexadecimal | 0x | 0x | 0xFF = 255 |
Bitwise Operations in Practice
Once you understand binary, bitwise operations become straightforward. They're used to pack multiple boolean flags into a single integer, work with hardware registers, implement fast modulo-by-powers-of-2, and manipulate color channel values.
// Packing permission flags into one integer
const READ = 0b001; // 1
const WRITE = 0b010; // 2
const EXECUTE = 0b100; // 4
const permissions = READ | WRITE; // 0b011 = 3
// Checking a flag
if (permissions & READ) { /* user can read */ }
if (permissions & EXECUTE) { /* user can execute */ } // false
// Extracting color channels from a 32-bit RGBA integer
const color = 0xFF5733FF;
const red = (color >> 24) & 0xFF; // 255
const green = (color >> 16) & 0xFF; // 87
const blue = (color >> 8) & 0xFF; // 51
const alpha = color & 0xFF; // 255
Two's Complement: How Computers Store Negative Numbers
Binary as described above only represents positive integers. Real computers use two's complement to represent negative numbers using the same bit patterns — with the most significant bit acting as a sign bit.
To negate a number in two's complement:
- Write the positive number in binary
- Flip all the bits (one's complement)
- Add 1
8-bit two's complement examples:
0 = 00000000
1 = 00000001
2 = 00000010
127 = 01111111 ← max positive value for 8-bit signed
-1 = 11111111 ← flip 00000001, add 1 → 11111110 + 1 = 11111111
-2 = 11111110
-128 = 10000000 ← min negative value for 8-bit signed
This is why 8-bit signed integers range from -128 to 127 (not -127 to 127), and why JavaScript bitwise operations work on 32-bit signed integers — the sign bit means -1 >>> 0 produces 4294967295 (the 32-bit unsigned interpretation).
Two's complement is also the reason the Year 2038 problem exists: Unix timestamps stored as 32-bit signed integers will overflow from the max positive value (2,147,483,647 = Jan 19 2038 03:14:07 UTC) to the max negative value, wrapping to a date in 1901. Modern systems use 64-bit timestamps to avoid this.
