Loading learning content...
SET forces a bit to 1. CLEAR forces a bit to 0. But what if you want to flip a bit—turn 0 into 1 and 1 into 0—without knowing its current state? This is the toggle operation, and it's powered by the XOR (exclusive or) operator.
Toggling is uniquely elegant: it requires no conditional logic, no checking the current bit value, and executes in a single operation. Press a button to turn a light on; press again to turn it off. This light switch behavior emerges naturally from XOR's mathematical properties.
In this page, we'll explore the toggle operation, understand why XOR creates the perfect flip, discover the self-inverse property that makes toggle special, and see how this operation enables everything from simple UI switches to sophisticated cryptographic algorithms.
By the end of this page, you will understand: (1) The formula n ^ (1 << i) for toggling bit i, (2) Why XOR is the perfect operator for flipping bits, (3) The self-inverse property: toggle(toggle(n, i), i) = n, (4) Multiple toggle patterns and their uses, (5) Real-world applications in switches, encryption, and error detection, and (6) The unique characteristics that distinguish toggle from set and clear.
Toggling a bit means inverting its value: if it's 0, make it 1; if it's 1, make it 0. This is done without knowing the current state.
Given:
n (our target number)i (the bit position to toggle)Goal:
i is 0, return a value where it's 1i is 1, return a value where it's 0The Formula:
result = n ^ (1 << i)
This formula has two components:
(1 << i) — Creates a mask with only bit i set to 1n ^ mask — Performs bitwise XOR to flip bits where mask is 1, preserve where mask is 0Let's trace through this with examples.
Example 1: Toggle bit 2 in 5 (bit 2 is currently 1) n = 5: 0 0 0 0 0 1 0 1 (bits 0 and 2 are set)mask (1<<2): 0 0 0 0 0 1 0 0 (bit 2 mask) ^ ─────────────────Result: 0 0 0 0 0 0 0 1 = 1 Bit 2 was 1, now it's 0. The bit flipped! --- Example 2: Toggle bit 2 in 1 (bit 2 is currently 0) n = 1: 0 0 0 0 0 0 0 1 (only bit 0 is set)mask (1<<2): 0 0 0 0 0 1 0 0 (bit 2 mask) ^ ─────────────────Result: 0 0 0 0 0 1 0 1 = 5 Bit 2 was 0, now it's 1. The bit flipped! Notice: The same operation flips in either direction.The XOR (exclusive or) operator returns 1 when exactly one of its inputs is 1, and 0 otherwise. This truth table reveals why XOR is the perfect toggle operator.
XOR Truth Table:
| A | B | A XOR B |
|---|---|---|
| 0 | 0 | 0 |
| 0 | 1 | 1 |
| 1 | 0 | 1 |
| 1 | 1 | 0 |
Key Insight: Look at XOR with 1:
0 XOR 1 = 1 (0 becomes 1)1 XOR 1 = 0 (1 becomes 0)XOR with 1 always flips! And XOR with 0 always preserves:
0 XOR 0 = 0 (stays 0)1 XOR 0 = 1 (stays 1)This is exactly what we need:
Understanding XOR behavior at each bit position: Original n: 1 0 1 1 0 0 1 0 (n = 178)mask (1<<4): 0 0 0 1 0 0 0 0 (toggle bit 4) ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ For each position, apply XOR:Pos 7: 1 XOR 0 = 1 (preserved: mask is 0)Pos 6: 0 XOR 0 = 0 (preserved: mask is 0)Pos 5: 1 XOR 0 = 1 (preserved: mask is 0)Pos 4: 1 XOR 1 = 0 (FLIPPED: was 1, now 0) ← Toggle!Pos 3: 0 XOR 0 = 0 (preserved: mask is 0)Pos 2: 0 XOR 0 = 0 (preserved: mask is 0)Pos 1: 1 XOR 0 = 1 (preserved: mask is 0)Pos 0: 0 XOR 0 = 0 (preserved: mask is 0) Result: 1 0 1 0 0 0 1 0 (n = 162) Only bit 4 changed (1→0). All other bits unchanged.Think of XOR as 'flip where mask says flip'. If the mask bit is 1, the corresponding bit gets inverted. If the mask bit is 0, the original bit passes through unchanged. It's like a selective inversion operator.
Toggle has a remarkable mathematical property that neither SET nor CLEAR possesses: it is self-inverse. Toggling twice returns to the original state.
Mathematical Statement:
(n ^ mask) ^ mask = n
This means:
This property is NOT true for SET or CLEAR:
Why This Matters:
Demonstrating self-inverse property: Start: 0 0 0 0 0 1 0 1 = 5 Toggle bit 2: 0 0 0 0 0 0 0 1 = 1 (bit 2: 1→0) Toggle bit 2: 0 0 0 0 0 1 0 1 = 5 (bit 2: 0→1) We're back to 5! Mathematical proof:Let's trace bit by bit for position 2: Original bit: 1First XOR 1: 1 XOR 1 = 0Second XOR 1: 0 XOR 1 = 1 ← Back to original! This works for both cases:- 0 → 1 → 0- 1 → 0 → 1 Toggle is its own inverse: toggle ∘ toggle = identity123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
// Toggle bit at position i// Returns a new value with bit i flipped function toggleBit(n: number, i: number): number { return n ^ (1 << i);} // In-place modificationfunction toggleBitInPlace(value: { n: number }, i: number): void { value.n ^= (1 << i); // Equivalent to: value.n = value.n ^ (1 << i)} // Demonstrating self-inverse propertyconst original = 42;console.log(`Original: ${original} = ${original.toString(2)}`); const toggled = toggleBit(original, 3);console.log(`After toggle bit 3: ${toggled} = ${toggled.toString(2)}`); const restored = toggleBit(toggled, 3);console.log(`After toggle again: ${restored} = ${restored.toString(2)}`); console.log(`Self-inverse: ${original === restored}`); // true // Multiple toggles demonstrate the patternlet value = 0;console.log(`\nToggle sequence on bit 0:`);for (let i = 0; i < 6; i++) { console.log(` Step ${i}: ${value}`); value = toggleBit(value, 0);}// 0 → 1 → 0 → 1 → 0 → 1 (alternating!) ---python# Toggle bit at position i# Returns a new value with bit i flipped def toggle_bit(n: int, i: int) -> int: return n ^ (1 << i) # Demonstrating self-inverse propertyoriginal = 42print(f"Original: {original} = {bin(original)}") toggled = toggle_bit(original, 3)print(f"After toggle bit 3: {toggled} = {bin(toggled)}") restored = toggle_bit(toggled, 3)print(f"After toggle again: {restored} = {bin(restored)}") print(f"Self-inverse: {original == restored}") # True # Multiple toggles demonstrate the patternvalue = 0print("\nToggle sequence on bit 0:")for i in range(6): print(f" Step {i}: {value}") value = toggle_bit(value, 0)# 0 → 1 → 0 → 1 → 0 → 1 (alternating!) ---javapublic class ToggleBit { // Toggle bit at position i // Returns a new value with bit i flipped public static int toggleBit(int n, int i) { return n ^ (1 << i); } public static void main(String[] args) { int original = 42; System.out.println("Original: " + original + " = " + Integer.toBinaryString(original)); int toggled = toggleBit(original, 3); System.out.println("After toggle bit 3: " + toggled + " = " + Integer.toBinaryString(toggled)); int restored = toggleBit(toggled, 3); System.out.println("After toggle again: " + restored + " = " + Integer.toBinaryString(restored)); System.out.println("Self-inverse: " + (original == restored)); // true // Multiple toggles int value = 0; System.out.println("\nToggle sequence on bit 0:"); for (int i = 0; i < 6; i++) { System.out.println(" Step " + i + ": " + value); value = toggleBit(value, 0); } }} ---cpp#include <iostream>#include <bitset> // Toggle bit at position i// Returns a new value with bit i flippedint toggleBit(int n, int i) { return n ^ (1 << i);} int main() { int original = 42; std::cout << "Original: " << original << " = " << std::bitset<8>(original) << std::endl; int toggled = toggleBit(original, 3); std::cout << "After toggle bit 3: " << toggled << " = " << std::bitset<8>(toggled) << std::endl; int restored = toggleBit(toggled, 3); std::cout << "After toggle again: " << restored << " = " << std::bitset<8>(restored) << std::endl; std::cout << "Self-inverse: " << std::boolalpha << (original == restored) << std::endl; // true // Multiple toggles int value = 0; std::cout << "\nToggle sequence on bit 0:" << std::endl; for (int i = 0; i < 6; i++) { std::cout << " Step " << i << ": " << value << std::endl; value = toggleBit(value, 0); } return 0;}Unlike SET and CLEAR, toggle is NOT idempotent. toggle(toggle(n, i), i) ≠ toggle(n, i). Toggling twice returns to the original, not to the same toggled state. This is by design—it's what makes toggle useful for switches.
Like SET and CLEAR, toggling multiple bits is most efficient with a combined mask.
Approach 1: Sequential (Less Efficient)
n = n ^ (1 << i);
n = n ^ (1 << j);
n = n ^ (1 << k);
Approach 2: Combined Mask (More Efficient)
mask = (1 << i) | (1 << j) | (1 << k);
n = n ^ mask;
Building the mask uses OR (as for SET), but applying it uses XOR. Each 1 in the mask causes that position to flip.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
// Toggle multiple bits function toggleBits(n: number, positions: number[]): number { let mask = 0; for (const pos of positions) { mask |= (1 << pos); // Build the toggle mask } return n ^ mask; // Apply with XOR} // Visualizationfunction visualizeToggle(n: number, positions: number[]): void { let mask = 0; for (const pos of positions) { mask |= (1 << pos); } const result = n ^ mask; console.log(`Toggle positions ${positions} in ${n.toString(2).padStart(8, '0')}`); console.log(`Mask: ${mask.toString(2).padStart(8, '0')}`); console.log(`Result: ${result.toString(2).padStart(8, '0')}`); console.log();} // Example: Toggle bits 0, 2, and 4visualizeToggle(0b11111111, [0, 2, 4]);// Before: 11111111// Mask: 00010101// Result: 11101010 (bits 0, 2, 4 flipped from 1→0) visualizeToggle(0b00000000, [0, 2, 4]);// Before: 00000000// Mask: 00010101// Result: 00010101 (bits 0, 2, 4 flipped from 0→1) // Toggle all bits (full inversion)function toggleAllBits(n: number, bitWidth: number = 8): number { const allOnes = (1 << bitWidth) - 1; return n ^ allOnes;} console.log("Toggle all 8 bits of 0b10101010:");console.log(`Before: ${(0b10101010).toString(2).padStart(8, '0')}`);console.log(`After: ${toggleAllBits(0b10101010).toString(2).padStart(8, '0')}`);// Before: 10101010// After: 01010101 (complete inversion) // Self-inverse with multiple bitsconst original = 0b11001100;const toggled = toggleBits(original, [1, 3, 5, 7]);const restored = toggleBits(toggled, [1, 3, 5, 7]);console.log(`\nSelf-inverse test:`);console.log(`Original: ${original.toString(2).padStart(8, '0')}`);console.log(`Toggled: ${toggled.toString(2).padStart(8, '0')}`);console.log(`Restored: ${restored.toString(2).padStart(8, '0')}`);console.log(`Match: ${original === restored}`); ---python# Toggle multiple bits def toggle_bits(n: int, positions: list[int]) -> int: mask = 0 for pos in positions: mask |= (1 << pos) # Build the toggle mask return n ^ mask # Apply with XOR # Visualizationdef visualize_toggle(n: int, positions: list[int]) -> None: mask = 0 for pos in positions: mask |= (1 << pos) result = n ^ mask print(f"Toggle positions {positions} in {bin(n)[2:].zfill(8)}") print(f"Mask: {bin(mask)[2:].zfill(8)}") print(f"Result: {bin(result)[2:].zfill(8)}") print() # Example: Toggle bits 0, 2, and 4visualize_toggle(0b11111111, [0, 2, 4])# Before: 11111111# Mask: 00010101# Result: 11101010 visualize_toggle(0b00000000, [0, 2, 4])# Before: 00000000# Mask: 00010101# Result: 00010101 # Toggle all bits (full inversion)def toggle_all_bits(n: int, bit_width: int = 8) -> int: all_ones = (1 << bit_width) - 1 return n ^ all_ones print("Toggle all 8 bits of 0b10101010:")print(f"Before: {bin(0b10101010)[2:].zfill(8)}")print(f"After: {bin(toggle_all_bits(0b10101010))[2:].zfill(8)}")# Before: 10101010# After: 01010101 # Self-inverse with multiple bitsoriginal = 0b11001100toggled = toggle_bits(original, [1, 3, 5, 7])restored = toggle_bits(toggled, [1, 3, 5, 7])print(f"\nSelf-inverse test:")print(f"Original: {bin(original)[2:].zfill(8)}")print(f"Toggled: {bin(toggled)[2:].zfill(8)}")print(f"Restored: {bin(restored)[2:].zfill(8)}")print(f"Match: {original == restored}")Now we have three fundamental bit operations. Let's compare them systematically to solidify your understanding.
| Property | SET | CLEAR | TOGGLE |
|---|---|---|---|
| Formula | n | (1 << i) | n & ~(1 << i) | n ^ (1 << i) |
| Operator | OR (|) | AND (&) with NOT (~) | XOR (^) |
| Effect on 0 | 0 → 1 | 0 → 0 (no change) | 0 → 1 |
| Effect on 1 | 1 → 1 (no change) | 1 → 0 | 1 → 0 |
| Idempotent? | Yes | Yes | No |
| Self-inverse? | No | No | Yes |
| Needs current state? | No | No | No |
| Result depends on current? | Partially | Partially | Yes |
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354
// Complete demonstration of SET, CLEAR, TOGGLE behavior function setBit(n: number, i: number): number { return n | (1 << i);} function clearBit(n: number, i: number): number { return n & ~(1 << i);} function toggleBit(n: number, i: number): number { return n ^ (1 << i);} function demonstrate(n: number, i: number): void { const bit = (n >> i) & 1; console.log(`n = ${n.toString(2).padStart(8, '0')}, bit ${i} = ${bit}`); console.log(` SET: ${setBit(n, i).toString(2).padStart(8, '0')} (always 1 at position ${i})`); console.log(` CLEAR: ${clearBit(n, i).toString(2).padStart(8, '0')} (always 0 at position ${i})`); console.log(` TOGGLE: ${toggleBit(n, i).toString(2).padStart(8, '0')} (flipped at position ${i})`); console.log();} // Test with bit initially 0console.log("=== Bit initially 0 ===");demonstrate(0b00000000, 2); // Test with bit initially 1 console.log("=== Bit initially 1 ===");demonstrate(0b00000100, 2); // Demonstrating idempotence vs self-inverseconsole.log("=== Repeated operations ===");let n = 0b00000000;const pos = 3; // SET is idempotentn = 0;n = setBit(n, pos);n = setBit(n, pos);console.log(`SET twice: ${n.toString(2).padStart(8, '0')} (same as SET once)`); // CLEAR is idempotentn = 0b11111111;n = clearBit(n, pos);n = clearBit(n, pos);console.log(`CLEAR twice: ${n.toString(2).padStart(8, '0')} (same as CLEAR once)`); // TOGGLE is self-inversen = 0b00000000;n = toggleBit(n, pos);console.log(`TOGGLE once: ${n.toString(2).padStart(8, '0')}`);n = toggleBit(n, pos);console.log(`TOGGLE twice: ${n.toString(2).padStart(8, '0')} (back to original!)`);Use SET when you want to ensure a bit is 1 (grant, enable, add). Use CLEAR when you want to ensure a bit is 0 (revoke, disable, remove). Use TOGGLE when you want to flip the current state (switches, alternation, XOR operations).
Toggle's self-inverse property makes it uniquely suited for certain applications. Let's explore practical uses.
Toggle is natural for UI switches and settings where clicking alternates between on and off.
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768
// Settings panel with toggle switches enum Setting { DARK_MODE = 0, NOTIFICATIONS = 1, AUTO_SAVE = 2, SOUND_EFFECTS = 3, ANIMATIONS = 4, HIGH_CONTRAST = 5, LARGE_TEXT = 6, DEVELOPER_MODE = 7,} class SettingsManager { private settings: number; constructor(initialSettings: number = 0) { this.settings = initialSettings; } toggle(setting: Setting): boolean { this.settings ^= (1 << setting); // TOGGLE return this.isEnabled(setting); } isEnabled(setting: Setting): boolean { return (this.settings & (1 << setting)) !== 0; } enable(setting: Setting): void { this.settings |= (1 << setting); // SET } disable(setting: Setting): void { this.settings &= ~(1 << setting); // CLEAR } getActiveSettings(): string[] { return Object.entries(Setting) .filter(([k, v]) => typeof v === 'number' && this.settings & (1 << v)) .map(([k]) => k); } exportSettings(): number { return this.settings; } importSettings(settings: number): void { this.settings = settings; }} // Simulate user interactionsconst settings = new SettingsManager(); console.log("Initial settings:", settings.getActiveSettings()); // User clicks togglesconsole.log("\nUser toggles DARK_MODE:", settings.toggle(Setting.DARK_MODE) ? "ON" : "OFF");console.log("User toggles NOTIFICATIONS:", settings.toggle(Setting.NOTIFICATIONS) ? "ON" : "OFF");console.log("User toggles DARK_MODE again:", settings.toggle(Setting.DARK_MODE) ? "ON" : "OFF"); console.log("\nActive settings:", settings.getActiveSettings()); // Save and restore settingsconst savedSettings = settings.exportSettings();console.log(`\nExported settings (decimal): ${savedSettings}`);console.log(`Exported settings (binary): ${savedSettings.toString(2).padStart(8, '0')}`);XOR has several mathematical properties that make it invaluable in bit manipulation. Understanding these properties enables creative solutions to many problems.
a ^ 0 = a — XOR with zero preserves the valuea ^ a = 0 — XOR with itself gives zeroa ^ b = b ^ a — Order doesn't matter(a ^ b) ^ c = a ^ (b ^ c) — Grouping doesn't mattera ^ b ^ b = a — XOR twice with same value recovers originala ^ 1 = ~a (for single bit) — XOR with 1 flips1234567891011121314151617181920212223242526272829303132333435363738394041
// Demonstrating XOR properties const a = 42;const b = 73;const c = 15; // Property 1: Identity (a ^ 0 = a)console.log("Identity: a ^ 0 = a");console.log(` ${a} ^ 0 = ${a ^ 0}`);console.log(` Equal: ${a === (a ^ 0)}\n`); // Property 2: Self-inverse (a ^ a = 0)console.log("Self-inverse: a ^ a = 0");console.log(` ${a} ^ ${a} = ${a ^ a}\n`); // Property 3: Commutative (a ^ b = b ^ a)console.log("Commutative: a ^ b = b ^ a");console.log(` ${a} ^ ${b} = ${a ^ b}`);console.log(` ${b} ^ ${a} = ${b ^ a}`);console.log(` Equal: ${(a ^ b) === (b ^ a)}\n`); // Property 4: Associative ((a ^ b) ^ c = a ^ (b ^ c))console.log("Associative: (a ^ b) ^ c = a ^ (b ^ c)");console.log(` (${a} ^ ${b}) ^ ${c} = ${(a ^ b) ^ c}`);console.log(` ${a} ^ (${b} ^ ${c}) = ${a ^ (b ^ c)}`);console.log(` Equal: ${((a ^ b) ^ c) === (a ^ (b ^ c))}\n`); // Property 5: Inverse recovery (a ^ b ^ b = a)console.log("Inverse recovery: a ^ b ^ b = a");console.log(` ${a} ^ ${b} ^ ${b} = ${a ^ b ^ b}`);console.log(` Equal to a: ${(a ^ b ^ b) === a}\n`); // Application: Find the single unique numberfunction findSingleNumber(nums: number[]): number { // All duplicates cancel out due to a ^ a = 0 return nums.reduce((acc, n) => acc ^ n, 0);} console.log("Find single number (others appear twice):");console.log(` [2, 3, 2, 4, 3] → ${findSingleNumber([2, 3, 2, 4, 3])}`); // 4console.log(` [1, 1, 2, 2, 3] → ${findSingleNumber([1, 1, 2, 2, 3])}`); // 3You've now mastered the toggle operation and explored the powerful properties of XOR. Let's consolidate the key concepts.
n ^ (1 << i) toggles bit i—flipping 0→1 or 1→0.n ^ mask ^ mask = n. Toggling twice returns to original—the 'undo' is built in.| Operation | Formula | Operator | Idempotent? | Self-Inverse? |
|---|---|---|---|---|
| CHECK | (n >> i) & 1 | Shift + AND | N/A | N/A |
| SET | n | (1 << i) | OR | Yes | No |
| CLEAR | n & ~(1 << i) | AND + NOT | Yes | No |
| TOGGLE | n ^ (1 << i) | XOR | No | Yes |
What's Next:
With check, set, clear, and toggle mastered individually, the next page explores combining these operations—building complex bit manipulation sequences, conditional operations, and real-world patterns that use multiple operations together.
You now command all four fundamental bit operations: check, set, clear, and toggle. The toggle operation's self-inverse property makes it unique among the three modifying operations. Next, we'll learn to combine these operations for powerful compound bit manipulation.