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⁸2561 byte max + 1
2^10 = 2¹⁰1,0241 KiB (kibibyte)
2^16 = 2¹⁶65,536Max ports, 2 bytes
2^20 = 2²⁰1,048,5761 MiB (mebibyte)
2^32 = 2³²4,294,967,296IPv4 address space

Hexadecimal (Base-16)

Hex Digit Values

HexDecBinary HexDecBinary
000000881000
110001991001
220010A101010
330011B111011
440100C121100
550101D131101
660110E141110
770111F151111

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

OctalDecimalBinary
00000
11001
22010
33011
44100
55101
66110
77111

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:

  1. Invert all bits (one's complement)
  2. 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
---0000No permissions
--x0011Execute only
-w-0102Write only
-wx0113Write and execute
r--1004Read only
r-x1015Read and execute
rw-1106Read and write
rwx1117Full 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)

DecHexOctBinary DecHexOctBinary
0000000016102010000
1110000117112110001
2220001018122210010
3330001119132310011
4440010020142410100
5550010121152510101
6660011022162610110
7770011123172710111
88100100024183011000
99110100125193111001
10A1201010261A3211010
11B1301011271B3311011
12C1401100281C3411100
13D1501101291D3511101
14E1601110301E3611110
15F1701111311F3711111

Common Hex Values to Memorize

Hex Decimal Significance
0x000Null byte
0x0A10Newline (LF)
0x0D13Carriage return (CR)
0x2032Space character
0x30-0x3948-57ASCII digits 0-9
0x41-0x5A65-90ASCII A-Z
0x61-0x7A97-122ASCII a-z
0x7F127DEL character, max 7-bit
0x80128High bit set
0xFF255Max byte value
0x100256First value requiring 2 bytes
0x40010241 KiB
0xFFFF65535Max 16-bit value
0xDEADBEEF3735928559Common debug marker
0xCAFEBABE3405691582Java 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":

Document Version: 1.0
Last Updated: November 2025
Author: Linux Documentation Project - Binghamton University

← Back to NumberSystems Index ↑ Back to EXPANDED