A Practical Guide to Bitwise Operations: AND, OR, XOR, NOT and Shifts
Learn how bitwise AND, OR, XOR, NOT and bit shifts really work, with worked binary examples, flag and mask tricks, and a free calculator that shows decimal, hex and binary side by side.
A Practical Guide to Bitwise Operations: AND, OR, XOR, NOT and Shifts
Bit-level math is one of those topics that feels intimidating until you watch the columns line up. Once you can see which bits flipped, AND, OR, XOR, NOT and the shift operators stop being abstract and turn into everyday tools for flags, masks, packed fields and network addresses. This guide walks through what each operator does, where you actually reach for it, and a worked example you can reproduce in the Bitwise Calculator in under a minute.
A quick grounding fact before the operators: in C and most languages that inherit its memory model, the standard fixes char at exactly 8 bits (the CHAR_BIT macro in <limits.h> is mandated to be at least 8, and is 8 on every mainstream platform). That 8-bit byte is why we keep coming back to widths of 8, 16, 32 and 64 — they are byte multiples, and they decide what "flip every bit" even means.
AND, OR and XOR: combining two operands
These three operators take two values, A and B, and compare them column by column.
- AND keeps a
1only where both operands have a1. It is your masking tool:value AND 0x0Fstrips everything except the low four bits (the low nibble). - OR keeps a
1where either operand has a1. It is how you set bits:flags OR 0b00000100guarantees bit 2 is on without disturbing the rest. - XOR sets a
1only where the two operands differ. That "difference" property makes it the workhorse of toggling, parity, checksums, and the no-temporary swap trick.
Here is the canonical XOR example. Take A = 5 and B = 3:
A = 5 = 101
B = 3 = 011
---
XOR = 110 = 6
The top and middle columns differ, the bottom column matches, so the result is 110, which is 6. If you type 5, pick XOR, and type 3 into the calculator, the result panel shows 6 in decimal, 0x6 in hex and 0b110 in binary all at once, with A, B and the result stacked so you can confirm the two columns that changed.
NOT and why it needs a bit width
NOT (the unary complement) flips every bit. The catch is that "every bit" depends on how wide your number is. Mathematically an integer has infinitely many leading zeros, so flipping them all would produce a negative number under two's complement. That is rarely what you want when you are reasoning about a register.
So you pick a width. In an 8-bit window, NOT 0 is 255 (all eight bits become 1). In a 16-bit window, the same NOT 0 is 65535. The width is not decoration — it is the boundary that decides where the bits stop. A good calculator makes the width explicit precisely so you do not accidentally read NOT 0 as -1.
The width also drives unsigned right shift (>>> in many languages), which zero-fills from the top of that same window. This is where a lot of bugs hide: plain right shift keeps the sign bit, so a negative number stays negative, while unsigned right shift treats the value as a raw pattern in the chosen width and pads with zeros.
Shifts: multiplying, dividing and packing
Shifting moves every bit a fixed number of places.
- Left shift by one moves each bit up and drops a
0into the bottom — the same as multiplying by 2. So3 << 4is48, exactly3 × 16. - Right shift by one divides by 2 and discards the remainder.
256 >> 2is64.
Shifts shine when you pack several small fields into one integer — say, storing R, G and B channels in a single number. You left-shift each field into its slot, OR the pieces together, and later AND-plus-shift to pull a field back out. Trying that round trip by hand is exactly the kind of thing you want to verify before it goes into code.
A worked example: reading a register field
Say a datasheet tells you a control register currently holds 0xA5, and that bits 4–6 store a 3-bit clock divisor. You want just that field. The recipe is: AND with a mask that isolates bits 4–6, then right-shift it down to bit 0.
Step one, mask. The mask for bits 4–6 is 0x70 (0b01110000). Enter A = 0xA5, operation AND, B = 0x70:
A = 0xA5 = 1010 0101
mask = 0x70 = 0111 0000
---------
AND = 0x20 = 0010 0000
The calculator reports 0x20, decimal 32, binary 0b100000. Step two, shift that result right by 4 to read the field as a plain number: 0x20 >> 4 gives 2. So the divisor field holds the value 2. The bit-by-bit panel pads everything to your chosen width, so you can literally count the columns and confirm you touched only bits 4–6.
I reach for this constantly when I am poking at hardware I half-understand. The first time I wired up an I²C sensor, I kept misreading a status byte because I was shifting before masking instead of after. Watching the binary rows update as I swapped the order was what finally made the mistake obvious — the stray high bits were sitting right there in the panel, impossible to ignore.
Where bitwise math pays off
Beyond registers, the same operators carry a surprising amount of real work:
- Network math is bitwise underneath. AND an address with a subnet mask like
0xFFFFFF00to find the network portion, then read the binary view to count the leading ones. It pairs naturally with an IP to decimal conversion when you are checking whether two hosts share a subnet. - Permissions are bit fields too. Unix file modes pack read, write and execute into octal triples, which is just three bits per group toggled with OR and read with AND.
- Feature flags in application code are often a single integer where each bit is one option, set with OR and cleared by ANDing with the complement.
The reason a dedicated calculator helps is that it keeps decimal, hex and binary in view simultaneously. You stop translating in your head — you mask, you shift, and the three representations update together. Because the operands, the operation and the width all ride in the URL, a shared link reopens the exact same calculation, which is handy for code review or a quick "is this right?" to a colleague. Try it on your next register read with the Bitwise Calculator, and keep it open the next time a subnet mask refuses to make sense.
Made by Toolora · Updated 2026-06-13