WierdX — Programming Reference All tutorials →
Developer reference · Practical tutorials · CS fundamentals
CS Fundamentals

Binary and Bitwise Operations Every Developer Should Know

Everything inside a computer is stored as bits — ones and zeros. Most of the time higher-level abstractions hide this detail, but understanding binary representation and bitwise operators lets you read network packet headers, implement compact flag fields, and optimize inner loops where every cycle matters.

Published June 30, 2026

Modern programming rarely requires manual bit manipulation, but the cases where it does tend to be fundamental: parsing binary file formats, writing network protocol handlers, managing hardware registers, or storing a set of boolean flags in a single integer. Understanding this layer also makes hex output in debuggers and error logs readable rather than opaque.

Binary representation

Decimal uses ten digits (0–9) and each position represents a power of ten. Binary uses two digits (0 and 1) and each position represents a power of two.

Decimal 42 in binary:
  Position: 5   4   3   2   1   0
  Power:    32  16  8   4   2   1
  Bit:       1   0   1   0   1   0
  Value:    32 + 0 + 8 + 0 + 2 + 0 = 42

Python verification:
>>> bin(42)
'0b101010'
>>> int('101010', 2)
42
>>> 0b101010
42

Hexadecimal (base 16) is a compact notation for binary: each hex digit maps exactly to four bits. This is why hex appears in color codes, memory addresses, and network masks — it is a human-readable shorthand for binary patterns.

0xFF   = 0b11111111 = 255
0x0F   = 0b00001111 =  15
0xDEAD = 0b1101111010101101 = 57005

Two's complement: how signed integers work

Unsigned integers use all bits to represent magnitude. Signed integers use two's complement: the highest bit is a sign bit (0 for positive, 1 for negative), and negative numbers are represented as the bitwise complement plus one.

In an 8-bit signed integer:
   127 =  0b01111111   (max positive)
     1 =  0b00000001
     0 =  0b00000000
    -1 =  0b11111111   (all bits set)
  -128 =  0b10000000   (most negative)

Two's complement of 1 to get -1:
  Step 1 -- flip all bits of  0b00000001 to get 0b11111110
  Step 2 -- add 1:            0b11111110 + 1  = 0b11111111  (-1)

Two's complement is clever because addition and subtraction work the same way whether the numbers are signed or unsigned. The hardware does not need separate circuits for the two cases.

Bitwise operators

The six bitwise operators work on individual bits. Understanding them requires reading the truth table for AND, OR, and XOR:

  • AND (&) — output bit is 1 only when both input bits are 1
  • OR (|) — output bit is 1 when either input bit is 1
  • XOR (^) — output bit is 1 when input bits differ
  • NOT (~) — flips every bit
  • Left shift (<<) — shifts bits left, equivalent to multiplying by 2
  • Right shift (>>) — shifts bits right, equivalent to integer-dividing by 2
a = 0b1100   # 12
b = 0b1010   # 10

a & b   # 0b1000  =  8  (AND: both bits set)
a | b   # 0b1110  = 14  (OR: either bit set)
a ^ b   # 0b0110  =  6  (XOR: bits differ)
~a      # -13 in Python (two's complement: flip all bits)

1 << 4  # 16  (1 shifted left 4 positions = 2^4)
64 >> 2 # 16  (64 divided by 4)

Bit flags: packing booleans into one integer

A common pattern uses individual bits as boolean flags. One integer can hold 32 or 64 independent flags, each readable and writable without affecting the others. File permission systems, network packet headers, and graphics APIs all use this technique.

READ    = 0b001   # 1
WRITE   = 0b010   # 2
EXECUTE = 0b100   # 4

permissions = READ | WRITE   # 0b011 = 3, means read + write

# Check whether a specific permission is set
def has_permission(perm, flag):
    return (perm & flag) != 0

has_permission(permissions, READ)     # True
has_permission(permissions, EXECUTE)  # False

# Add a permission
permissions |= EXECUTE    # now 0b111 = 7

# Remove a permission (AND with the complement of the flag)
permissions &= ~WRITE     # now 0b101 = 5 (read + execute)

Practical XOR tricks

XOR has two useful properties: a ^ a == 0 (anything XORed with itself is zero) and a ^ 0 == a (anything XORed with zero is unchanged). These properties appear in several well-known algorithms.

# Find the one number that appears an odd number of times in a list
def find_odd_one_out(nums):
    result = 0
    for n in nums:
        result ^= n   # numbers that appear twice cancel out (n ^ n == 0)
    return result

find_odd_one_out([2, 3, 2, 4, 3])   # 4

# Swap two integers without a temporary variable
a, b = 5, 9
a ^= b   # a = 5 ^ 9
b ^= a   # b = 9 ^ (5 ^ 9) = 5
a ^= b   # a = (5 ^ 9) ^ 5 = 9
# a == 9, b == 5

XOR is also the core of most stream ciphers and simple checksums: a byte XORed with a key byte produces the ciphertext, and XORing the ciphertext with the same key recovers the original.

When to use bitwise operations

In application code, clarity beats cleverness. A dict of booleans or a dataclass with named flags is easier to read than a bit field. Reach for bitwise operations when you are interoperating with a protocol or API that packs flags into integers, when memory is genuinely scarce, or when you are implementing a standard algorithm that uses them. Knowing how they work is also essential for understanding low-level output from debuggers, network packet captures, and binary file format specifications.