Number Systems: Binary, Octal, and Hexadecimal Arithmetic
Purpose: Understanding alternative number systems is fundamental to system administration, programming, and understanding how computers represent and manipulate data. This guide covers binary (base-2), octal (base-8), and hexadecimal (base-16) number systems with practical applications in Linux/Unix environments.
Why These Number Systems Matter
Computers operate in binary - everything is ultimately 1s and 0s. However, reading long strings of binary is impractical for humans. Octal and hexadecimal provide compact representations that map cleanly to binary:
| System |
Base |
Digits |
Common Uses |
| Binary |
2 |
0, 1 |
Machine code, bit flags, permissions analysis |
| Octal |
8 |
0-7 |
Unix file permissions (chmod 755) |
| Hexadecimal |
16 |
0-9, A-F |
Memory addresses, MAC addresses, colors, IPv6 |
Key Insight: One octal digit = exactly 3 binary bits. One hex digit = exactly 4 binary bits. This makes conversion between these systems trivial once you understand the pattern.
Understanding Place Values
Decimal (Base-10) Review
In decimal, each position is a power of 10:
Number: 4 2 5
│ │ └── 5 × 10⁰ = 5 × 1 = 5
│ └──── 2 × 10¹ = 2 × 10 = 20
└────── 4 × 10² = 4 × 100 = 400
───
Total: 425
Binary (Base-2)
Each position is a power of 2:
Binary: 1 1 0 1 0 1 0 1
│ │ │ │ │ │ │ └── 1 × 2⁰ = 1 × 1 = 1
│ │ │ │ │ │ └──── 0 × 2¹ = 0 × 2 = 0
│ │ │ │ │ └────── 1 × 2² = 1 × 4 = 4
│ │ │ │ └──────── 0 × 2³ = 0 × 8 = 0
│ │ │ └────────── 1 × 2⁴ = 1 × 16 = 16
│ │ └──────────── 0 × 2⁵ = 0 × 32 = 0
│ └────────────── 1 × 2⁶ = 1 × 64 = 64
└──────────────── 1 × 2⁷ = 1 × 128 = 128
───
Total: 213
Powers of 2 Reference
| Power (2^n) |
Value |
Common Name |
| 2^0 = 2⁰ | 1 | |
| 2^1 = 2¹ | 2 | |
| 2^2 = 2² | 4 | |
| 2^3 = 2³ | 8 | |
| 2^4 = 2⁴ | 16 | |
| 2^5 = 2⁵ | 32 | |
| 2^6 = 2⁶ | 64 | |
| 2^7 = 2⁷ | 128 | |
| 2^8 = 2⁸ | 256 | 1 byte max + 1 |
| 2^10 = 2¹⁰ | 1,024 | 1 KiB (kibibyte) |
| 2^16 = 2¹⁶ | 65,536 | Max ports, 2 bytes |
| 2^20 = 2²⁰ | 1,048,576 | 1 MiB (mebibyte) |
| 2^32 = 2³² | 4,294,967,296 | IPv4 address space |
Hexadecimal (Base-16)
Hex Digit Values
| Hex | Dec | Binary |
Hex | Dec | Binary |
| 0 | 0 | 0000 | 8 | 8 | 1000 |
| 1 | 1 | 0001 | 9 | 9 | 1001 |
| 2 | 2 | 0010 | A | 10 | 1010 |
| 3 | 3 | 0011 | B | 11 | 1011 |
| 4 | 4 | 0100 | C | 12 | 1100 |
| 5 | 5 | 0101 | D | 13 | 1101 |
| 6 | 6 | 0110 | E | 14 | 1110 |
| 7 | 7 | 0111 | F | 15 | 1111 |
Hex to Decimal Conversion
Example: Convert 0x2A3F to decimal
Hex: 2 A 3 F
│ │ │ └── F × 16⁰ = 15 × 1 = 15
│ │ └─────── 3 × 16¹ = 3 × 16 = 48
│ └──────────── A × 16² = 10 × 256 = 2,560
└───────────────── 2 × 16³ = 2 × 4096 = 8,192
──────
Total: 10,815
Hex to Binary (Direct Mapping)
Each hex digit converts directly to 4 binary bits:
Example: Convert 0xDEADBEEF to binary
Hex: D E A D B E E F
│ │ │ │ │ │ │ │
Binary: 1101 1110 1010 1101 1011 1110 1110 1111
Result: 11011110101011011011111011101111
Common Hex Notations
| Notation |
Example |
Context |
| 0x prefix |
0xFF |
C, Python, most programming |
| \x prefix |
\xFF |
Escape sequences in strings |
| h suffix |
FFh |
Assembly language |
| # prefix |
#FF5733 |
HTML/CSS colors |
| Colon-separated |
00:1A:2B:3C:4D:5E |
MAC addresses |
Octal (Base-8)
Octal Digit Values
| Octal | Decimal | Binary |
| 0 | 0 | 000 |
| 1 | 1 | 001 |
| 2 | 2 | 010 |
| 3 | 3 | 011 |
| 4 | 4 | 100 |
| 5 | 5 | 101 |
| 6 | 6 | 110 |
| 7 | 7 | 111 |
Octal to Decimal Conversion
Example: Convert 0755 to decimal
Octal: 7 5 5
│ │ └── 5 × 8⁰ = 5 × 1 = 5
│ └─────── 5 × 8¹ = 5 × 8 = 40
└──────────── 7 × 8² = 7 × 64 = 448
───
Total: 493
Octal to Binary (Direct Mapping)
Each octal digit converts directly to 3 binary bits:
Example: Convert 0755 to binary
Octal: 7 5 5
│ │ │
Binary: 111 101 101
Result: 111101101
Unix Permissions Connection: This is why file permissions work so naturally in octal. Each permission set (owner, group, other) is 3 bits: read (4), write (2), execute (1). So rwxr-xr-x = 111 101 101 = 755.
Binary Arithmetic
Binary Addition
Rules: 0+0=0, 0+1=1, 1+0=1, 1+1=10 (0 carry 1)
Example: 1011 + 1101
1 1 1 ← carries
1 0 1 1 (11 in decimal)
+ 1 1 0 1 (13 in decimal)
─────────
1 1 0 0 0 (24 in decimal)
Binary Subtraction
Rules: 0-0=0, 1-0=1, 1-1=0, 0-1=1 (borrow 1)
Example: 1101 - 1001
1 1 0 1 (13 in decimal)
- 1 0 0 1 (9 in decimal)
─────────
0 1 0 0 (4 in decimal)
Binary Multiplication
Same as decimal: multiply and shift
Example: 101 × 11
1 0 1 (5 in decimal)
× 1 1 (3 in decimal)
───────
1 0 1 (101 × 1)
1 0 1 (101 × 1, shifted left)
───────
1 1 1 1 (15 in decimal)
Two's Complement (Negative Numbers)
Computers represent negative numbers using two's complement:
- Invert all bits (one's complement)
- Add 1
Example: Represent -5 in 8-bit two's complement
+5 in binary: 0000 0101
Invert all bits: 1111 1010
Add 1: + 0000 0001
───────────
-5 in binary: 1111 1011
Why Two's Complement? It allows the same addition circuit to work for both positive and negative numbers. Adding -5 and +5 gives 0 (with overflow discarded).
Hexadecimal Arithmetic
Hex Addition
Add digits, carry 16 (remember: A=10, B=11, C=12, D=13, E=14, F=15)
Example: 0x3A7 + 0x1C9
1 1 ← carries
3 A 7
+ 1 C 9
───────
5 7 0
Step by step:
7 + 9 = 16 = 0x10 → write 0, carry 1
A + C + 1 = 10 + 12 + 1 = 23 = 0x17 → write 7, carry 1
3 + 1 + 1 = 5 → write 5
Result: 0x570 (1392 in decimal)
Hex Subtraction
Example: 0xA3 - 0x4F
A 3
- 4 F
─────
5 4
Step by step:
3 - F: Can't do it, borrow 16 from A
(3 + 16) - F = 19 - 15 = 4
(A - 1) - 4 = 9 - 4 = 5
Result: 0x54 (84 in decimal)
Bitwise Operations
These operations work on individual bits and are fundamental to system programming:
AND (&)
Result is 1 only if both bits are 1
1010 1100
& 1100 1010
───────────
1000 1000
Use: Masking bits, checking if specific bit is set
OR (|)
Result is 1 if either bit is 1
1010 1100
| 1100 1010
───────────
1110 1110
Use: Setting bits, combining flags
XOR (^)
Result is 1 if bits are different
1010 1100
^ 1100 1010
───────────
0110 0110
Use: Toggling bits, simple encryption, swap without temp variable
NOT (~)
Inverts all bits
~ 1010 1100
───────────
0101 0011
Use: Creating masks, two's complement
Left Shift (<<)
Shifts bits left, fills with zeros (multiply by 2 per shift)
0000 1011 << 2
──────────────
0010 1100
11 << 2 = 44 (11 × 4)
Right Shift (>>)
Shifts bits right (divide by 2 per shift)
0010 1100 >> 2
──────────────
0000 1011
44 >> 2 = 11 (44 ÷ 4)
Linux/Unix Tools for Number Conversion
printf Command
# Decimal to hex
printf "%x\n" 255
# Output: ff
# Decimal to octal
printf "%o\n" 255
# Output: 377
# Hex to decimal
printf "%d\n" 0xff
# Output: 255
# Octal to decimal
printf "%d\n" 0377
# Output: 255
# With formatting
printf "Hex: 0x%X Octal: 0%o Binary: " 255 255
# Output: Hex: 0xFF Octal: 0377 Binary:
bc Calculator
# Set input base (ibase) and output base (obase)
echo "obase=16; 255" | bc
# Output: FF
echo "obase=2; 255" | bc
# Output: 11111111
echo "ibase=16; obase=2; FF" | bc
# Output: 11111111
echo "ibase=2; 11111111" | bc
# Output: 255
# Hex arithmetic
echo "ibase=16; A + B" | bc
# Output: 21 (in decimal)
bc Gotcha: When using both ibase and obase, set obase FIRST. Once you set ibase, all subsequent numbers (including the obase value) are interpreted in that base!
Bash Built-in Arithmetic
# Hex to decimal
echo $((0xff))
# Output: 255
# Octal to decimal
echo $((0377))
# Output: 255
# Binary to decimal (Bash 4+)
echo $((2#11111111))
# Output: 255
# Arithmetic in different bases
echo $((0xff + 0x10))
# Output: 271
# Base conversion with parameter expansion
decimal=255
printf -v hex "%x" $decimal
echo $hex
# Output: ff
Python One-liners
# Decimal to hex
python3 -c "print(hex(255))"
# Output: 0xff
# Decimal to binary
python3 -c "print(bin(255))"
# Output: 0b11111111
# Decimal to octal
python3 -c "print(oct(255))"
# Output: 0o377
# Hex to decimal
python3 -c "print(int('ff', 16))"
# Output: 255
# Binary to decimal
python3 -c "print(int('11111111', 2))"
# Output: 255
# Interactive calculations
python3 -c "print(0xff & 0xf0)"
# Output: 240
xxd - Hex Dump Utility
# Create hex dump of file
xxd filename
# Reverse: convert hex dump back to binary
xxd -r hexdump.txt > binary_file
# Plain hex output
xxd -p filename
# Binary output (bits)
xxd -b filename
od - Octal Dump
# Octal dump
od filename
# Hex dump
od -x filename
# With ASCII
od -c filename
# Decimal
od -d filename
Practical Applications
File Permissions (Octal)
Permission: rwx r-x r-x
Binary: 111 101 101
Octal: 7 5 5
chmod 755 filename
| Permission |
Binary |
Octal |
Meaning |
| --- | 000 | 0 | No permissions |
| --x | 001 | 1 | Execute only |
| -w- | 010 | 2 | Write only |
| -wx | 011 | 3 | Write and execute |
| r-- | 100 | 4 | Read only |
| r-x | 101 | 5 | Read and execute |
| rw- | 110 | 6 | Read and write |
| rwx | 111 | 7 | Full permissions |
Special Permission Bits (The Fourth Octal Digit)
Unix permissions actually use 4 octal digits, not 3. The leading digit controls special permission bits:
Full permission: 4755
│└┴┴── Standard permissions (rwxr-xr-x)
└───── Special bits (setuid)
In ls -l output: -rwsr-xr-x
^
s = setuid bit set (execute becomes 's')
| Bit |
Octal |
Binary |
Symbol |
Effect |
| Setuid |
4 |
100 |
s (in owner execute) |
Execute as file owner, not as user running it |
| Setgid |
2 |
010 |
s (in group execute) |
Execute as file group; on directories, new files inherit group |
| Sticky |
1 |
001 |
t (in other execute) |
On directories, only owner can delete files (e.g., /tmp) |
Common examples:
chmod 4755 /usr/bin/passwd # setuid - runs as root
chmod 2775 /shared/project # setgid - files inherit group
chmod 1777 /tmp # sticky - users can't delete others' files
Combined: chmod 6755 file # setuid + setgid (4+2=6)
Display Note: When special bits are set but execute is not, you see uppercase S or T instead of lowercase s or t. For example: -rwSr--r-- means setuid is set but owner execute is not.
IP Addresses and Subnet Masks
Subnet mask /24 in different formats:
Decimal: 255.255.255.0
Binary: 11111111.11111111.11111111.00000000
Hex: FF.FF.FF.00
CIDR /24 means 24 bits set to 1
Calculating network address (IP AND Mask):
IP: 192.168.1.100
11000000.10101000.00000001.01100100
Mask /24: 11111111.11111111.11111111.00000000
─────────────────────────────────────
Network: 11000000.10101000.00000001.00000000
192.168.1.0
MAC Addresses (Hex)
MAC: 00:1A:2B:3C:4D:5E
6 bytes = 48 bits
Each pair is one byte (8 bits)
00 = 0000 0000
1A = 0001 1010
2B = 0010 1011
... etc
Memory Addresses
Typical 64-bit address: 0x7FFE5C3A1B20
Breaking it down:
7FFE 5C3A 1B20
Each hex digit = 4 bits
Full address = 48 bits used (of 64 available)
Color Codes (Hex)
Color: #FF5733 (a red-orange)
FF = Red = 255 (max)
57 = Green = 87
33 = Blue = 51
Each component: 0-255 (00-FF)
ASCII and Unicode
Character 'A':
Decimal: 65
Hex: 0x41
Binary: 0100 0001
Octal: 101
In shell:
printf '\x41' # prints A
printf '\101' # prints A (octal)
echo $'\x41' # prints A
Quick Reference: Conversion Table (0-31)
| Dec | Hex | Oct | Binary |
Dec | Hex | Oct | Binary |
| 0 | 0 | 0 | 00000 | 16 | 10 | 20 | 10000 |
| 1 | 1 | 1 | 00001 | 17 | 11 | 21 | 10001 |
| 2 | 2 | 2 | 00010 | 18 | 12 | 22 | 10010 |
| 3 | 3 | 3 | 00011 | 19 | 13 | 23 | 10011 |
| 4 | 4 | 4 | 00100 | 20 | 14 | 24 | 10100 |
| 5 | 5 | 5 | 00101 | 21 | 15 | 25 | 10101 |
| 6 | 6 | 6 | 00110 | 22 | 16 | 26 | 10110 |
| 7 | 7 | 7 | 00111 | 23 | 17 | 27 | 10111 |
| 8 | 8 | 10 | 01000 | 24 | 18 | 30 | 11000 |
| 9 | 9 | 11 | 01001 | 25 | 19 | 31 | 11001 |
| 10 | A | 12 | 01010 | 26 | 1A | 32 | 11010 |
| 11 | B | 13 | 01011 | 27 | 1B | 33 | 11011 |
| 12 | C | 14 | 01100 | 28 | 1C | 34 | 11100 |
| 13 | D | 15 | 01101 | 29 | 1D | 35 | 11101 |
| 14 | E | 16 | 01110 | 30 | 1E | 36 | 11110 |
| 15 | F | 17 | 01111 | 31 | 1F | 37 | 11111 |
Common Hex Values to Memorize
| Hex |
Decimal |
Significance |
| 0x00 | 0 | Null byte |
| 0x0A | 10 | Newline (LF) |
| 0x0D | 13 | Carriage return (CR) |
| 0x20 | 32 | Space character |
| 0x30-0x39 | 48-57 | ASCII digits 0-9 |
| 0x41-0x5A | 65-90 | ASCII A-Z |
| 0x61-0x7A | 97-122 | ASCII a-z |
| 0x7F | 127 | DEL character, max 7-bit |
| 0x80 | 128 | High bit set |
| 0xFF | 255 | Max byte value |
| 0x100 | 256 | First value requiring 2 bytes |
| 0x400 | 1024 | 1 KiB |
| 0xFFFF | 65535 | Max 16-bit value |
| 0xDEADBEEF | 3735928559 | Common debug marker |
| 0xCAFEBABE | 3405691582 | Java class file magic number |
Mental Math Tricks
Quick Binary to Decimal
Use the "doubling" method - start from left, double and add:
Binary: 1 0 1 1 0 1
Start with leftmost bit: 1
Double and add next: 1×2 + 0 = 2
Double and add next: 2×2 + 1 = 5
Double and add next: 5×2 + 1 = 11
Double and add next: 11×2 + 0 = 22
Double and add next: 22×2 + 1 = 45
101101 binary = 45 decimal
Quick Decimal to Binary
Repeatedly divide by 2, read remainders bottom-up:
45 ÷ 2 = 22 remainder 1 ↑
22 ÷ 2 = 11 remainder 0 │
11 ÷ 2 = 5 remainder 1 │
5 ÷ 2 = 2 remainder 1 │
2 ÷ 2 = 1 remainder 0 │
1 ÷ 2 = 0 remainder 1 │
│
Read up: 101101
Hex Mental Math
Think of hex digits as "almost tens":
- 0xA = 10 (just remember A=10)
- 0xF = 15 (F for Fifteen)
- 0x10 = 16 (one "hex-teen")
- 0x100 = 256 (16 × 16)
- 0x1000 = 4096 (16³)
Document Version: 1.0
Last Updated: November 2025
Author: Linux Documentation Project - Binghamton University
← Back to NumberSystems Index
↑ Back to EXPANDED