WikiPlus

Binary Math for Beginners: AND, OR, XOR Operations

Bitwise operations are among the most powerful tools in a programmer's arsenal, yet they confuse many beginners because they seem abstract until you see them applied. AND, OR, XOR, NOT, and bit shifts operate directly on the binary representations of integers, enabling tricks that are both faster and more expressive than their arithmetic equivalents. This guide teaches each operation with clear examples and shows you where they appear in real code.

Understanding Bitwise AND, OR, and NOT

Bitwise operations apply a logical operation to each pair of corresponding bits in two numbers independently. Understanding the truth table for each operation is the key to reading and writing bitwise code. Bitwise AND (&): A bit in the result is 1 only if the corresponding bits in BOTH inputs are 1. Think of it as multiplication: 1 AND 1 = 1, anything else = 0. Truth table: 0&0=0, 0&1=0, 1&0=0, 1&1=1. Example: 12 & 10 in binary: 1100 & 1010 = 1000 = 8 decimal. Only bit position 3 has a 1 in both inputs. Bitwise OR (|): A bit in the result is 1 if the corresponding bit is 1 in EITHER input. Truth table: 0|0=0, 0|1=1, 1|0=1, 1|1=1. Example: 12 | 10 in binary: 1100 | 1010 = 1110 = 14 decimal. Any position with a 1 in either input becomes 1. Bitwise NOT (~): Inverts every bit. Applies to a single number. ~n = -(n+1) in two's complement (the representation used by computers for signed integers). ~0 = -1. ~5 = -6. In binary: ~00000101 = 11111010 (as an 8-bit signed value, this is -6). These three operations have direct applications: AND is used to read (mask) specific bits, OR is used to set specific bits, and NOT is used to create inverse masks.

XOR: The Exclusive OR Operation

XOR (exclusive OR, written as ^ in most languages) is one of the most versatile bitwise operations. A bit in the result is 1 if the corresponding bits in the two inputs are DIFFERENT. If they are the same (both 0 or both 1), the result bit is 0. Truth table: 0^0=0, 0^1=1, 1^0=1, 1^1=0. Example: 12 ^ 10 in binary: 1100 ^ 1010 = 0110 = 6 decimal. XOR has several remarkable mathematical properties: Property 1: n ^ n = 0. Any number XOR itself is 0. Every bit is the same, so every result bit is 0. Property 2: n ^ 0 = n. Any number XOR 0 is the number itself. 0 bits have no effect on XOR. Property 3: XOR is reversible. If a ^ b = c, then c ^ a = b and c ^ b = a. This makes XOR useful for encryption and data swapping. Practical applications of XOR: Swapping without a temporary variable: In Python/JavaScript: a ^= b; b ^= a; a ^= b. After these three operations, a and b have swapped values without needing a temp variable. This works because of XOR's reversibility. Finding the unique element: Given an array where every number appears twice except one, XOR all elements together. All pairs cancel out (n ^ n = 0) and the unique element remains. Simple encryption: XOR-ing data with a key produces encrypted output. XOR-ing the encrypted output with the same key recovers the original data. This is how OTP (One-Time Pad) and stream cipher XOR encryption work at the bit level. Toggling bits: n ^ mask flips all bits where mask has a 1.

Bit Shifts: Left Shift and Right Shift

Bit shift operations move all bits in a number left or right by a specified number of positions. They are equivalent to multiplication or division by powers of 2. Left Shift (<<): Shifts bits to the left, filling the right with zeros. n << k multiplies n by 2^k. Example: 1 << 4 = 16 (binary: 0001 → 10000). Example: 5 << 2 = 20 (binary: 101 → 10100). Left shifts lose the leftmost bit if it shifts out of the integer's range. Right Shift (>>): Shifts bits to the right. For unsigned values or positive signed values, right-shifts fill the left with zeros, and n >> k divides n by 2^k (integer division). Example: 20 >> 2 = 5 (binary: 10100 → 00101). For signed negative values, arithmetic right shift fills with the sign bit (1) to preserve the sign. Why bit shifts matter: Performance: Multiplication and division by powers of 2 via bit shifts are faster at the hardware level than general multiplication/division. Modern compilers optimize multiplication by constants to shifts automatically, but knowing shifts helps you write clearer bit-level code. Extracting nibbles from bytes: (0xAB >> 4) gives 0xA (the upper nibble). 0xAB & 0x0F gives 0xB (the lower nibble). Together, these extract the two hex digits from a byte. Building bitmasks: 1 << n creates a mask with only bit n set. (1 << 8) - 1 creates a mask for the lower 8 bits (0xFF). These patterns appear constantly in low-level code. Iterating over bits: To test each bit of an integer, combine a loop counter with left shift: for (int i = 0; i < 8; i++) { if (n & (1 << i)) { /* bit i is set */ } }.

Practical Bitmask Patterns in Real Code

Armed with AND, OR, XOR, NOT, and shifts, you can implement a range of practical patterns. Checking if a number is even or odd: n & 1. If the result is 1, the number is odd (least significant bit is set). If 0, the number is even. This is faster than n % 2. Setting a specific bit to 1: n | (1 << k) sets bit k. Example: to set bit 3 of the value 0b10010101: 0b10010101 | (1 << 3) = 0b10011101. Clearing a specific bit to 0: n & ~(1 << k) clears bit k. The mask (1 << k) has a 1 only at position k. NOT of that mask has 0 only at position k. AND-ing with n clears bit k without affecting others. Toggling a specific bit: n ^ (1 << k) flips bit k without affecting others. Checking if a specific bit is set: (n >> k) & 1. Shift bit k to the rightmost position, then check if it is 1. Flag bitmask pattern: Define constants as powers of 2. const READABLE = 1; const WRITABLE = 2; const EXECUTABLE = 4. Set flags: permissions |= WRITABLE. Clear flags: permissions &= ~EXECUTABLE. Check flags: if (permissions & READABLE) {...}. Rounding down to a power of 2: n & ~(k - 1) aligns n down to a multiple of k (where k is a power of 2). For byte alignment: n & ~7 gives the 8-byte-aligned address at or below n. Our Number Base Converter lets you see the binary representation of intermediate values in these operations — useful when learning or debugging bitwise code.

Frequently Asked Questions

When should I use XOR instead of NOT in programming?
Use XOR (^) to toggle specific bits without affecting others: n ^ mask flips exactly the bits where mask has a 1. Use NOT (~) to flip all bits of a value at once, often to create an inverted mask. In practice, NOT is most commonly used as part of a clear-bit operation: n & ~(1 << k) clears bit k. Standalone NOT is less common because it inverts the sign of integers in two's complement, which can be surprising.
What is the difference between >> and >>> in JavaScript?
In JavaScript, >> is a signed right shift that preserves the sign bit: shifting a negative number right fills with 1s. >>> is an unsigned right shift that always fills with 0s, treating the number as unsigned. For -1 (all bits set in two's complement): -1 >> 1 = -1 (still all 1s), but -1 >>> 1 = 2147483647 (fills with 0). Use >>> when you want to treat integers as unsigned bit patterns, such as when extracting the upper bits of a hash value.
Can bitwise operations work on floating-point numbers?
In most languages, bitwise operations only work on integers. In JavaScript, applying bitwise operators to floating-point numbers first truncates them to 32-bit integers: 5.7 | 0 = 5. This is actually a common JS trick for fast floor division of positive numbers. For languages like C, you can use memcpy or union tricks to access the raw bits of a float, but this is advanced usage for specific performance scenarios.