skip to content

questions

4

How do you clear bit k of a status word with a mask, leaving every other bit untouched?

level: juniorimportance: must knowfreq 78%

answer

  1. Which operator forces a bit off?
  2. Mask must leave the others alone
  3. AND with 0 clears, AND with 1 keeps
  4. The mask needs a hole at k
  5. Invert the shifted single-bit mask

basics

~20 s

Use n = n & ~(1 << k). The shifted mask holds a single 1 at position k; inverting it gives all ones except there, so the AND forces that bit to 0 and leaves the rest unchanged.

solid answer

~50 s

Build the single-bit mask `1 << k`, invert it, and AND: `n = n & ~(1 << k)`. The reasoning is the operator identities — `x AND 1 = x` and `x AND 0 = 0` — so a mask of all ones with one hole zeroes exactly that position and leaves the rest alone. The companions follow the same shape: set with `n | (1 << k)` (`x OR 1 = 1`), toggle with `n ^ (1 << k)` (`x XOR 1` flips), test with `(n & (1 << k)) != 0` or `(n >> k) & 1`. This matters in any read-modify-write on a word you do not own outright — a device status register, a packed flags field — because AND-ing with the un-inverted mask, or XOR-ing to "clear", silently corrupts the neighbouring bits.

go deeper

for a junior

Recall the four one-liners cold — test, set, clear, toggle — and be able to say which operator forces a bit on, which forces it off, and which flips it. Getting the inversion in the clear right is the whole question.

for a middle

Explain the mechanics from the operator identities rather than from memory, and generalise to multi-bit fields: a run of k ones is (1 << k) - 1, and writing a field is clear-then-OR.

for a senior

Show read-modify-write judgment: on a shared status word you preserve bits you do not own, you never use toggle where you mean clear, and you keep mask width consistent with the word you are editing.

for a principal

Own the call between hand-written masks and a named accessor layer. Raw masks scattered through a codebase are a defect source; a small set of well-tested field helpers costs nothing at runtime and removes a whole bug class from review.

## The numbering, and the mask Bits in a word are numbered from 0 at the least significant end, and bit `k` carries place value `2^k`. The whole family of single-bit tricks is built from one mask, `1 << k`: a word that is zero everywhere except a single 1 at position `k`. Everything else falls out of the per-bit identity and annihilator of each operator: - `x AND 1 = x`, `x AND 0 = 0` → AND with 0 **forces a bit off**, AND with 1 **leaves it alone**. - `x OR 0 = x`, `x OR 1 = 1` → OR with 1 **forces a bit on**, OR with 0 **leaves it alone**. - `x XOR 0 = x`, `x XOR 1 = NOT x` → XOR with 1 **flips**, XOR with 0 **leaves it alone**. So the mask you AND with must be *mostly ones* (leave alone) with a *hole* at `k` (force off), while the mask you OR or XOR with must be *mostly zeros* with a *one* at `k`. | Goal | Expression | Why | |---|---|---| | Test bit k | `(n & (1 << k)) != 0`, or `(n >> k) & 1` | isolate the position, then compare against 0 (or bring it down to bit 0 first) | | Set bit k | `n \| (1 << k)` | OR with 1 forces on; zeros elsewhere leave the rest | | Clear bit k | `n & ~(1 << k)` | AND with 0 forces off; ones elsewhere leave the rest | | Toggle bit k | `n ^ (1 << k)` | XOR with 1 flips; zeros elsewhere leave the rest | | Write value v (0 or 1) | `(n & ~(1 << k)) \| (v << k)` | clear first, then OR the desired value in | ## The three bugs an interviewer is fishing for **"Clearing" with XOR.** `n ^ (1 << k)` clears the bit *only if it was already set*; if it was 0 the same expression sets it. XOR is a toggle, and a toggle is only a clear when you already know the current state. In a read-modify-write against a device register whose bits change underneath you, that is a coin flip. **Clearing with the un-inverted mask.** `n & (1 << k)` is a common slip. It does not clear bit `k` — it *isolates* it, zeroing all the other bits and keeping only that one. You have destroyed the whole word to preserve the bit you meant to erase. **Testing against 1.** `(n & (1 << k)) == 1` is false for every `k` except 0, because the AND yields either `0` or `1 << k` — the bit's *place value*, not the digit 1. Compare against 0, or shift the bit down to position 0 first with `(n >> k) & 1`. ## Multi-bit masks are the same idea A run of `k` low ones is `(1 << k) - 1`: subtracting one from a lone set bit borrows all the way down, lighting every position below it. That gives you the two standard field operations — clear the low `k` bits with `n & ~((1 << k) - 1)`, and extract the low `k` bits with `n & ((1 << k) - 1)`. A field of width `w` starting at offset `k` uses `((1 << w) - 1) << k` as its mask, and the same clear-then-OR sequence writes a new value into it. ## Properties worth stating out loud Set and clear are **idempotent**: applying them twice is the same as applying them once, so re-issuing a command that sets an already-set flag is harmless. Toggle is **not** idempotent — it is its own inverse, which is exactly why it is the wrong tool for "make sure this is off". None of these operations reads or writes any position other than the ones the mask marks, which is what makes them safe inside a read-modify-write on a shared status word: you preserve every flag you did not name. Finally, keep the mask width in mind. `1 << k` is computed in whatever word width the expression is evaluated in, so building a mask for a bit beyond that width does not give you a wider mask — it gives you a value the platform does not define usefully. Build masks in a type at least as wide as the word you are masking.

  • Write bit k to an arbitrary value v that is 0 or 1, in one expression and without a branch.
    `n = (n & ~(1 << k)) | (v << k)`. Clear the position first, then OR in the shifted value. The order matters: OR-ing before clearing cannot turn a 1 into a 0, so a lone `n | (v << k)` silently ignores `v = 0`. This form is branch-free and works for both values of `v`.
  • Why does the test `(n & (1 << k)) == 1` fail for most k?
    The AND yields either 0 or the bit's place value `2^k`, never the digit 1 — so the comparison only happens to work when `k` is 0. Compare against 0 instead (`!= 0`), or shift the bit down to position 0 first with `(n >> k) & 1` and then compare to 1.
  • How do you clear the lowest k bits of a word at once?
    `n & ~((1 << k) - 1)`. Subtracting 1 from the single-bit mask borrows downward and lights every position below `k`, giving a run of `k` ones; inverting that gives ones everywhere except the low `k` positions, so the AND zeroes exactly the low field and preserves the rest. The same mask, un-inverted, extracts that field.

The mask is a stencil laid over the word: AND paints zero through the one hole you cut, and the solid part of the stencil protects every other bit.

saying these in an interview costs you the question

  • Says XOR with the mask clears the bit
  • Writes n & (1 << k) to clear bit k
  • Expects a bit test to yield exactly 1 without shifting
  • Thinks OR can turn a bit off
  • Forgets to invert the mask before the AND
  • Builds the mask in a narrower word than the target

context

open as a page

What does n = n & (n - 1) do each pass of a bit-counting loop, and how many passes run?

level: middleimportance: must knowfreq 70%

basics

~20 s

Each pass clears the lowest set bit of n and leaves every other bit alone, so the loop runs exactly once per set bit — the population count — not once per bit of the word width.

open as a page

A capacity validator accepts n when (n & (n - 1)) == 0 — which inputs wrongly slip through?

level: middleimportance: should knowfreq 55%

basics

~20 s

Zero slips through, and on a signed word so does the most-negative value. The expression only asserts "at most one bit is set", so the real power-of-two test needs the guard n > 0 in front of it.

open as a page

A grid engine counts set bits in a 64-bit occupancy word a billion times a frame — is the clear-lowest-bit loop good enough?

level: seniorimportance: nice to knowfreq 30%

basics

~20 s

No. The loop costs one unpredictable branch per set bit, so its price rises with board density. At that call volume use a fixed-cost population-count instruction, with a branch-free shift-and-mask fallback where the hardware lacks one.

open as a page