Recommended resources
The freely available resources this lesson will direct you to.
Hello! In the last lesson, you interpreted whole bit patterns as signed integers. Now we’ll keep the bit pattern intact and work on selected positions within it. A single bit might record whether an option is enabled, a request has a particular property, or a device reports a particular status.
By the end of this lesson, you’ll be able to build a mask for one or more positions and use it to test, set, clear, or toggle those bits without unintentionally changing the others. You’ll also see how the same operations look in a small program.
Think in columns, not whole numbers
Bitwise operations compare corresponding bit positions. Unlike addition, a result in one column does not carry into another.
| First bit | Second bit | AND & |
OR | |
XOR ^ |
|---|---|---|---|---|
| 0 | 0 | 0 | 0 | 0 |
| 0 | 1 | 0 | 1 | 1 |
| 1 | 0 | 0 | 1 | 1 |
| 1 | 1 | 1 | 1 | 0 |
AND produces a 1 only when both inputs are 1. OR produces a 1 when either input is 1. XOR produces a 1 when the inputs differ. Bitwise NOT, written ~, reverses each bit. These symbols work on integers in both Python and C; they are different from logical operators such as C’s && and ||.
A mask is simply a bit pattern chosen to affect particular columns. Number bit positions from the right, starting at zero. For an eight-bit value, bit 0 has value 1, bit 1 has value 2, and bit 7 has value 128. Shifting a single 1 left by positions, written 1 << k, makes a mask for bit . For example, 1 << 2 is 00000100, or 0x04.
Read the operator introductions and the start of the masking discussion in Demystifying bitwise operations, a gentle C tutorial by Andre Inc. The diagrams are useful here: read each operation vertically, one pair of bits at a time, rather than treating the operands as decimal numbers.
Demystifying bitwise operations, a gentle C tutorial
Read the operator introductions and the beginning of “Masking” to connect the truth table to visual bit patterns.
In “Bitwise operations,” start with the operator overview. Then read the opening explanation and first illustration in each of “Bitwise AND,” “Bitwise OR,” “Bitwise XOR,” “Bitwise NOT,” “Left Shift,” and “Right Shift.” You can skip the longer code experiments and the later section on negative numbers. In “Masking,” begin at the mask definition. Continue through the illustration of a single-bit mask made with a left shift, stopping before the discussion of the pairwise-swap problem. Pay attention to why a mask’s zero bits and one bits play different roles depending on the operation.
A shift moves positions; a mask selects them. We’ll combine those ideas to inspect a bit, then use AND, OR, and XOR to control it.
Test a bit without changing the value
Take the eight-bit value 10100010, which is 0xA2. Suppose bit 2 indicates whether a particular flag is active. Its mask is 00000100 (0x04). AND keeps a 1 only where both the value and mask contain a 1:
10100010 value: 0xA2
& 00000100 mask: 0x04
--------
00000000
The result is zero, so bit 2 is clear. To ask whether it is set, write (value & mask) != 0. This produces a true-or-false answer; the AND expression itself produces an integer, not necessarily 1. For example, testing bit 5 of 0xA2 with mask 0x20 produces 0x20, which is nonzero.
If you need the selected bit as the number 0 or 1, move it to bit 0 and isolate that position: (value >> k) & 1. For bit 5 of 0xA2, the answer is 1. In these examples, applying & or >> computes a result; it does not modify value.
A mask can select several positions. For 0xA2, bits 5 and 2 have the combined mask 00100100 (0x24). Their meanings may differ, so distinguish two questions:
- Is at least one selected bit set?
(value & mask) != 0 - Are all selected bits set?
(value & mask) == mask
Here bit 5 is set and bit 2 is clear. The first condition is true; the second is false. This distinction becomes useful whenever one integer stores several independent flags.
Set, clear, and toggle
The same mask can change a selected bit, but the choice of operator determines how. Watch Jacob Sorber’s How do I Set, Clear, and Toggle a Single Bit? for a C demonstration. It begins from an existing bit-checking example; you do not need that earlier video to follow the changes to the value.
How do I Set, Clear, and Toggle a Single Bit?
Watch How do I Set, Clear, and Toggle a Single Bit? by Jacob Sorber to see each operation change a flag while unrelated bits remain in place.
Watch setting a bit with OR, then clearing a bit with AND and a complemented mask. In toggling a bit, notice why applying XOR twice restores the original value. Finish with assignment shorthand, which introduces |=, &=, and ^=.
Let mask = 1 << k, with a 1 at the target position and zeros elsewhere. Each operation follows directly from the truth table:
| Goal | Expression | Why other bits stay unchanged |
|---|---|---|
| Set bit to 1 | value | mask |
OR with 0 preserves a bit. |
| Clear bit to 0 | value & ~mask |
AND with 1 preserves a bit. |
| Toggle bit | value ^ mask |
XOR with 0 preserves a bit. |
For setting, the mask’s lone 1 forces the target column to 1 whether it was previously 0 or 1. Starting from 0xA2, setting bit 2 gives 10100110, or 0xA6.
Clearing requires one extra thought. ~mask has a zero at the target and ones elsewhere. AND with that pattern forces the target to zero but preserves every other bit. Starting from 0xA2, clearing bit 5 gives 10000010, or 0x82. Complementing the value instead would change all its bits; it is the mask that we complement.
Toggling is different from both: XOR with 1 reverses the target bit, regardless of its starting state. Toggling bit 2 of 0xA2 gives 0xA6; toggling bit 2 again gives 0xA2. Setting twice still leaves the bit set, and clearing twice still leaves it clear. Toggle is not a substitute for “make this bit active” when its current state is unknown.
In code, value |= mask is shorthand for assigning the result of value | mask back to value; similarly, &= and ^= update the stored value. Compare value |= mask with value = mask: the second statement replaces the entire value and loses every flag not present in the mask.
These operations also work on multiple bits at once. With mask = 0x24, value | mask sets both bits 5 and 2, value & ~mask clears both, and value ^ mask toggles both. The mask specifies where to act; the operator specifies what action to take.
A fixed-width sandbox
Python integers are not limited to eight bits, but we can deliberately keep value between 0x00 and 0xFF to model one byte. The following code shows each operation applied independently to the same starting value:
value = 0xA2 # 10100010
position = 2
if not 0 <= position < 8:
raise ValueError("position must be an eight-bit index")
mask = 1 << position # 00000100
print(f"start: {value:08b}")
print(f"bit 2: {(value >> position) & 1}")
print(f"set: {value | mask:08b}")
print(f"clear: {value & ~mask:08b}")
print(f"toggle: {value ^ mask:08b}")
flags = value
flags |= mask # update flags, rather than just computing a result
print(f"updated: {flags:08b}")
The starting bit 2 is zero. Consequently, setting and toggling it both display 10100110, while clearing it leaves 10100010 unchanged. The final line confirms that |= actually updates flags.
Two boundaries matter when taking these patterns into systems code. First, bit positions must fit the width you intend to use: an eight-bit value has positions 0 through 7. Second, ~mask deserves attention because Python’s integers do not have a built-in eight-bit width. In value & ~mask, the result is still confined to the bits present in our eight-bit value; if you need an explicitly eight-bit complemented mask, write (~mask) & 0xFF.
When you later use C for systems programming, prefer unsigned integer types for bit manipulation and validate shift positions before shifting. A bit pattern’s signed interpretation, which mattered in the previous lesson, should not accidentally determine how you build or shift a flag mask.
Key takeaways
A mask places ones at the positions you care about. AND tests selected bits; OR sets them; AND with a complemented mask clears them; and XOR toggles them. For a single bit at position , 1 << k builds the mask. Always distinguish a test that computes a result from an assignment that updates the stored value.
Next, we’ll move from positions within one value to the order of multiple bytes in memory: little-endian and big-endian representations.