Released · improving
Bit manipulation guide · 1/6
Bit manipulation treats an integer as a row of 0s and 1s and reads or changes individual positions directly. The CPU already stores integers in binary, so AND, OR, XOR and shifts usually compile to a single instruction. That makes bit tricks fast and compact, and it lets you express sets of on/off facts in very little code. This chapter covers binary representation, two's complement, the core operators and the vocabulary used in later chapters.
The integer 13 is 1101 in binary. The rightmost position is bit 0, and bit i is worth 2^i, so 1101 means 8 + 4 + 0 + 1.
| Bit index | 3 | 2 | 1 | 0 |
|---|---|---|---|---|
| Weight | 8 | 4 | 2 | 1 |
| Bits of 13 | 1 | 1 | 0 | 1 |
Bit 0 is the least significant bit (LSB); the leftmost 1 is the most significant bit (MSB).
print(bin(13)) # 0b1101
print(format(13, "08b")) # 00001101 (padded to 8 digits)
print(int("1101", 2)) # 13
print((13).bit_length()) # 4 bits neededAlmost every machine stores negative integers in two's complement: in n bits, -x is "flip every bit of x, then add 1", that is ~x + 1. With 8 bits, 5 is 00000101 and -5 is 11111011. One adder handles both signs and zero has a single encoding. An n-bit signed integer covers -2^(n-1) to 2^(n-1) - 1, so a 32-bit int runs from -2147483648 to 2147483647. A set top bit means the value is negative.
| Operation | Symbol | Result bit is 1 when | Example (12, 10) |
|---|---|---|---|
| AND | & | both bits are 1 | 1100 & 1010 = 1000 (8) |
| OR | vertical bar | at least one bit is 1 | 1100, 1010 give 1110 (14) |
| XOR | ^ | the bits differ | 1100 ^ 1010 = 0110 (6) |
| NOT | ~ | flips every bit | ~12 = -13 |
| Left shift | << | bits move left, zeros enter | 3 << 2 = 12 |
| Right shift | >> | bits move right | 12 >> 2 = 3 |
x << k equals x * 2^k, and for non-negative x, x >> k equals x // 2^k. Because of two's complement, ~x == -x - 1.
a, b = 12, 10
print(a & b, a | b, a ^ b) # 8 14 6
print(~a) # -13
print(3 << 2, 12 >> 2) # 12 3
print(-12 >> 1) # -6: arithmetic shift keeps the signAll four basic single-bit operations use the mask 1 << i:
x | (1 << i)x & ~(1 << i)x ^ (1 << i)(x >> i) & 1, or (x & (1 << i)) != 0x = 0b1010
print(bin(x | (1 << 0))) # 0b1011
print(bin(x & ~(1 << 1))) # 0b1000
print(bin(x ^ (1 << 3))) # 0b10
print((x >> 3) & 1) # 12^k via x & (2^k - 1)x & -x.The same expression can behave differently across languages. Python integers are unbounded, so 1 << 100 is exact, and negatives act as if they had infinitely many leading 1s. JavaScript converts operands to 32-bit signed integers first, so 1 << 31 is negative and >>> 0 reads a result as unsigned. Java's int is also 32 bits, with >>> as the zero-filling shift. In C++, unsigned types keep you clear of overflow and undefined behavior. The implementation and real-world chapters cover the details.
Bit manipulation views integers as bit arrays and works on them with AND, OR, XOR, NOT and shifts. Negative numbers use two's complement, so -x == ~x + 1. Once set, clear, toggle and test feel natural and you think in masks, subset enumeration and bitmask DP follow easily.
0 comments
Sign in · Sign in to leave a comment.
Be the first to comment.