Binary Numbers and Bitwise Operators
18 min · Intermediate
Need help? Ask a tutor on WhatsApp (opens in a new tab)Many coding-round problems hide a simple idea: a number is a row of bits. Once you can read and change those bits, tasks like flags, parity and subsets become a few short lines. Python adds one twist, its integers have no fixed size, and this lesson shows what that means.
Binary: a number as a row of bits
A bit is one binary digit, 0 or 1. An integer is a row of bits, and place i (counting from 0 on the right) is worth 2 to the power i.
A computer stores every integer as bits, each 0 or 1. Each place is worth a power of 2, so 1101 is 8 + 4 + 0 + 1. Python converts both ways.
n = 13print(bin(n))print(int("1101", 2))print(f"{n:08b}")Example 1 · Print the bits by hand
n = 37bits = []while n: bits.append(n & 1) n >>= 1print("".join(map(str, reversed(bits))))print(bin(37)[2:])100101 100101
What this code does
n & 1is the lowest bit.n >>= 1drops it. The loop collects bits from the right.- Reversing gives the usual left-to-right form, the same as
bin(37)without its0b.
bin gives text starting with 0b. int(text, 2) reads binary text back. The format 08b pads to 8 binary digits.
AND, OR and XOR work on every bit
A bitwise operator is an operator that works on the individual bits of integers: & (and), | (or), ^ (xor), ~ (not), << and >> (shifts).
The bitwise operators compare two numbers one bit at a time. AND keeps a 1 only when both have it, OR when either has it, XOR when exactly one has it.
a, b = 12, 10print(f"{a:04b} {b:04b}")print(a & b, a | b, a ^ b)Example 2 · Permission flags in one number
READ, WRITE, EXEC = 1, 2, 4perm = READ | WRITEprint(perm)print(bool(perm & WRITE), bool(perm & EXEC))perm ^= WRITEprint(perm)3 True False 1
What this code does
- Each permission is one bit, so OR combines them and AND tests them.
- XOR with WRITE toggles that bit: it was on, so now it is off and only READ is left.
Read it column by column: 1100 and 1010 give 1000 for AND (8), 1110 for OR (14) and 0110 for XOR (6).
Shifts and reading one bit
Shifting left by k moves every bit k places up, which multiplies by 2 to the power k. Shifting right divides by 2 and drops the remainder. To read bit i, shift it down and AND with 1.
print(5 << 1, 5 << 3, 40 >> 2, 7 >> 1)n = 0b10110print((n >> 1) & 1, (n >> 0) & 1)Example 3 · Set, clear and toggle one bit
n = 0b1010print(bin(n | (1 << 0)))print(bin(n & ~(1 << 1)))print(bin(n ^ (1 << 3)))0b1011 0b1000 0b10
What this code does
1 << iis a mask with only bit i on. OR with it sets bit 0.~(1 << 1)has every bit on except bit 1, so AND with it clears bit 1.- XOR flips bit 3 from 1 to 0, so 1010 becomes 10.
Bit 1 of 10110 is 1 and bit 0 is 0. Python gives << and >> lower priority than +, so write brackets when you mix them.
~ and negative numbers: no fixed size
An unlimited integer is Python's int, which grows as needed and never overflows. A negative number acts as if endless 1 bits sit on its left, and ~x is always -x - 1.
In languages with 32-bit integers, ~5 wraps around. A Python int has no fixed size, so it behaves as if endless 1 bits sit on the left of a negative number. The rule is simple: ~x equals -x - 1.
print(~5, ~-1, ~0)print(-5 & 0xFF, bin(-5 & 0xFF))print(-5 >> 1)Example 4 · A negative number as 8 bits
for x in (5, -5): print(x, format(x & 0xFF, "08b"))print(all(~x == -x - 1 for x in range(-50, 50)))5 00000101 -5 11111011 True
What this code does
- AND with
0xFFgives a fixed 8-bit view. 5 is 00000101 and -5 is 11111011. - The last line tests the rule ~x == -x - 1 on a hundred numbers.
AND with 0xFF keeps only the lowest 8 bits, which turns -5 into 251, the 8-bit pattern 11111011. Right shift rounds down, so -5 >> 1 is -3.
Next step
Check your understanding
10 quick questions