Bitwise Operations in Programming: AND, OR, XOR, NOT, and Bit Shifts Explained
Bitwise operations manipulate individual bits within integers and are essential in systems programming, embedded development, and performance-critical code. Learn how each operation works with practical examples.
What Are Bitwise Operations?
Bitwise operations treat integer values as sequences of individual bits and apply logical operations to each bit position in parallel. They are computed directly by the CPU in a single instruction, making them extremely fast.
They are used in:
- Flags and permissions (e.g., Unix file permissions:
rwxr-xr-x = 0755) - Network programming (subnet masks, IP addresses)
- Embedded systems (hardware register manipulation)
- Cryptography and hashing algorithms
- Performance optimisations (fast multiplication/division by powers of 2)
The Six Core Operations
AND (&)
Output bit is 1 only if both input bits are 1. Used to test or clear specific bits.
0110 1100
& 0011 1111
= 0010 1100
Use case — test if a bit is set:
const HAS_WRITE = 0b00000010;
if (permissions & HAS_WRITE) {
// write flag is set
}
OR (|)
Output bit is 1 if either input bit is 1. Used to set specific bits.
0110 1100
| 0011 1111
= 0111 1111
Use case — set a flag:
const FLAG_BOLD = 0b0001;
const FLAG_ITALIC = 0b0010;
let style = 0;
style = style | FLAG_BOLD; // set bold
style = style | FLAG_ITALIC; // set italic
XOR (^)
Output bit is 1 if the input bits are different. Used to toggle bits and in many cryptographic primitives.
0110 1100
^ 0011 1111
= 0101 0011
Use case — toggle a flag:
style = style ^ FLAG_BOLD; // toggle bold on/off
XOR swap trick (no temporary variable):
a = a ^ b;
b = a ^ b; // now b = original a
a = a ^ b; // now a = original b
NOT (~)
Inverts all bits. In JavaScript (32-bit signed integers), ~n = -(n + 1):
~5; // -6
~0; // -1
~-1; // 0
Use case — clear specific bits (AND with NOT of mask):
const FLAG_BOLD = 0b0001;
style = style & ~FLAG_BOLD; // clear bold flag
Left Shift (<<)
Shifts all bits left by N positions, filling vacated positions with 0. Equivalent to multiplying by 2ⁿ.
0000 0011 << 2 = 0000 1100 (3 × 4 = 12)
1 << 0; // 1 (2⁰)
1 << 1; // 2 (2¹)
1 << 4; // 16 (2⁴)
1 << 10; // 1024
Right Shift (>> and >>>)
Arithmetic right shift (>>): Preserves the sign bit (fills with 1 for negative numbers). Used for integers.
Logical right shift (>>>): Always fills with 0, regardless of sign. Treats the value as unsigned.
-8 >> 1; // -4 (arithmetic)
-8 >>> 1; // 2147483644 (logical, treats as unsigned 32-bit)
Compound Patterns
Check if a number is even or odd
n & (1 === 0); // even
n & (1 === 1); // odd
Check if a number is a power of 2
n > 0 && (n & (n - 1)) === 0;
Get the lowest set bit
n & -n;
Count set bits (population count / popcount)
function popcount(n) {
let count = 0;
n = n >>> 0; // treat as unsigned 32-bit
while (n) {
count += n & 1;
n >>>= 1;
}
return count;
}
JavaScript Gotcha: 32-Bit Integers
JavaScript numbers are 64-bit floats, but bitwise operators convert operands to signed 32-bit integers before operating. This means:
- No bitwise operation on a JavaScript number can produce a result outside the 32-bit signed range (−2,147,483,648 to 2,147,483,647)
- Large integers like
Number.MAX_SAFE_INTEGERget truncated
For 64-bit bitwise operations, use BigInt:
const a = 0xffffffffffffffffn; // BigInt
const b = a & 0x00000000ffffffffn;
Bitwise Flags in Real-World Code
Unix file permissions use a 12-bit field. The command chmod 755 sets:
- Owner:
rwx=0b111= 7 - Group:
r-x=0b101= 5 - Others:
r-x=0b101= 5
Combined: 0b111101101 = 0755 (octal).
The stat() syscall returns these permissions as an integer, and libraries compare them with bitwise AND to check specific permissions.