skip to content

Bit Manipulation & Math

Learn the binary layer beneath every integer and the small toolkit of math interviewers expect you to reason about without a library. Interviewers use these topics to probe whether you truly understand how numbers are represented and manipulated — the difference between memorizing a trick and explaining why it works.

part ofData structures & algorithmsoverview, primer and where to startread it →
on this pageshow

explore

questions

page 1 of 2

In a permission bitmask, how do you grant, revoke and test one flag without disturbing the others?

level: juniorimportance: must knowfreq 68%

answer

  1. three operations, three different operators
  2. one turns bits on, one turns them off
  3. the clear operator needs a complement
  4. toggling is not the same as clearing
  5. any-of versus all-of changes the comparison

basics

~20 s

Grant with OR: mask | FLAG. Revoke by ANDing the complement: mask & ~FLAG. Test membership with (mask & FLAG) != 0. XOR toggles rather than clears, so it is the wrong operator for revoke.

solid answer

~50 s

A flag constant is a single set bit, so the whole set of permissions lives in one integer. Grant is `mask = mask | FLAG` — OR only ever turns bits on, so every other permission survives. Revoke is `mask = mask & ~FLAG`: the complement has zeros only where FLAG has ones, so exactly that bit is cleared and everything else passes through untouched. Membership is `(mask & FLAG) != 0`, not `mask == FLAG` — the AND yields FLAG's own value when present and zero when absent, and equality against FLAG only works if no other permission is held. For multi-bit checks the two questions differ: `(mask & NEEDED) != 0` means holds any of them, `(mask & NEEDED) == NEEDED` means holds all of them. XOR flips a bit, so using it to revoke silently grants the permission when the holder did not already have it.

go deeper

for a junior

Be ready to write the three one-line operations from memory and say what each one does to the bits you are not touching. Knowing that OR sets, AND-with-complement clears, and XOR toggles is the whole answer at this level.

for a middle

Explain why a flag constant is a single set bit and why the membership test compares against zero rather than one. Be able to build both the any-of and the all-of test over a multi-bit requirement and say how they differ.

for a senior

Show the operational judgment: a revoke written with the toggling operator is a privilege-escalation bug that single-flag tests will not catch, and bit positions become a frozen contract the moment a mask is persisted or sent to a client.

for a principal

Own the boundary. Decide whether raw masks ever escape the module that owns the constants, insist that mutation goes through one small audited helper instead of hand-written bit twiddling at call sites, and set the policy for retiring flag positions.

## The representation A bitmask stores a set of up to w members in a single w-bit integer. Each member is assigned a fixed bit position, and its constant is that position's value: member 0 is `1 << 0` = 1, member 1 is `1 << 1` = 2, member 2 is `1 << 2` = 4, and so on. A permission set is then the bitwise OR of the constants it contains — `READ | WRITE | DEPLOY` might be `1 | 2 | 8` = 11, binary `1011`. Nothing else is stored: the number *is* the set. That identity is what makes the three basic operations one machine instruction each, and it is why the operators must be chosen for what they do to the bits you are *not* touching. ## Grant — OR `mask = mask | FLAG` OR produces a 1 wherever either side has a 1. Bits outside FLAG see `bit | 0`, which leaves them exactly as they were; the FLAG bit becomes 1 whether it was 0 or 1. Granting is therefore idempotent: granting a permission twice is the same as granting it once. Granting several at once is one operation — `mask | (READ | WRITE)`. The classic beginner error here is assignment instead of OR: `mask = FLAG` replaces the whole set with a single permission and quietly deletes everything else. ## Revoke — AND with the complement `mask = mask & ~FLAG` `~FLAG` (bitwise NOT) is all ones except a zero at FLAG's position. ANDing with it passes every other bit through unchanged (`bit & 1` = `bit`) and forces FLAG's bit to 0 (`bit & 0` = 0). Like grant, this is idempotent: revoking something the holder never had is a no-op. Revoking a group is `mask & ~(A | B)`. ## Why XOR is not revoke XOR yields 1 when exactly one side has a 1, so `mask ^ FLAG` *toggles*: on becomes off, and off becomes **on**. It looks correct in a test that first grants and then revokes, because toggling a set bit does clear it. It is wrong the moment revoke is called on a holder who does not have the permission — the operation grants it. In an access-control service that is a privilege-escalation bug produced by an operation named "remove", and it usually survives review because the happy-path test passes. XOR is the right operator only when you genuinely want a toggle, such as flipping a feature switch. ## Testing — compare against zero, or against the whole requirement `(mask & FLAG) != 0` AND keeps a bit only if both sides have it, so `mask & FLAG` is either FLAG's value or zero. Two errors are common. The first is `mask == FLAG`, which asks "is this the *only* permission held" and fails as soon as a second one is granted. The second is `(mask & FLAG) == 1`, which is true only for the lowest bit, because a set flag evaluates to its own value (8, 1024, …) rather than to 1. With a multi-bit requirement the test must say which question you are asking: | Intent | Expression | |---|---| | holds at least one of them | `(mask & NEEDED) != 0` | | holds all of them | `(mask & NEEDED) == NEEDED` | | holds none of them | `(mask & NEEDED) == 0` | | holds only these | `(mask & ~NEEDED) == 0` | Confusing the first two is the second-most-common bug in flag code after XOR-as-revoke, and it also fails safe-looking review: with a single-bit NEEDED the two expressions agree, so the difference only shows up once someone passes a union. ## Set algebra comes for free Because the mask is a set, role composition is arithmetic on words: the union of two roles is `roleA | roleB`, the permissions two roles share is `roleA & roleB`, what one role has that another lacks is `roleA & ~roleB`, and "is role A entirely contained in role B" is `(roleA & roleB) == roleA`. Each is a constant-time whole-set operation regardless of how many permissions are involved, which is the real reason services reach for masks on hot authorization paths. ## Operational care Bit positions are a permanent contract once a mask has been persisted or sent anywhere. Inserting a new permission in the middle of the constant list renumbers everything after it and silently reinterprets every stored value, so new flags are always appended at the next free position and retired ones leave a hole rather than being reused. Keep the constants and the grant/revoke/test helpers in one small module: hand-written bit twiddling scattered across call sites is where the toggle-instead-of-clear bug is born.

  • How would you revoke several permissions in a single operation?
    Build the union first, complement it once, and AND: `mask = mask & ~(DEPLOY | AUDIT)`. The complement has zeros exactly at those two positions, so both are cleared and every other permission passes through. Revoking flags one at a time in a loop is correct too, just needlessly repeated work on a hot path.
  • Why is the membership test written against zero rather than against one?
    A flag constant is `1 << k`, so ANDing it out yields its own value — 1024 for bit 10, not 1. Comparing to 1 is true only for the lowest bit and silently false for every other permission. Comparing against zero works for any position, and comparing against the flag itself works for a multi-bit requirement.
  • A teammate inserts a new permission constant in the middle of the list so it reads alphabetically. What breaks?
    Every constant after it shifts up one bit position, so every mask already stored or already sent to a client now decodes to different permissions — a stored 12 that meant AUDIT plus DEPLOY may now read as DEPLOY plus something nobody granted. Bit positions are a wire and storage contract: append new flags at the next free bit and never reuse a retired one.

Think of a row of light switches on one panel. OR flips the chosen switch up, AND with the complement flips it down, and XOR just flips whatever it finds — which is fine for a toggle and dangerous for an operation called revoke.

saying these in an interview costs you the question

  • Revokes with XOR, which grants the flag when it was absent
  • Tests with mask equals FLAG, which fails once a second flag is set
  • Assigns mask = FLAG to grant, wiping every other permission
  • Uses a non-zero AND result to mean the holder has all requested flags
  • Reuses a retired bit position for a new permission
  • Treats the logical and bitwise operators as interchangeable

context

open as a page

Why does an 8-bit two's-complement integer range from -128 to 127 rather than -127 to 127?

level: juniorimportance: must knowfreq 75%

basics

~20 s

Two's complement gives the top bit a weight of -128, so the 256 patterns split into 128 negatives (-128 to -1) and 128 non-negatives. Zero uses one non-negative pattern, leaving only 127 positives — so -128 has no positive counterpart.

open as a page

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

level: juniorimportance: must knowfreq 78%

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.

open as a page

Why does extracting a 12-bit length field from a packed 32-bit header need both a right shift and a mask?

level: juniorimportance: must knowfreq 68%

basics

~20 s

The shift moves the field down to bit 0 so it reads as a number; the mask clears the neighbouring fields still sitting above it. Shift alone leaves garbage on top, mask alone leaves the value scaled.

open as a page

Permutations vs combinations: which counts a top-3 podium from 30 entrants?

level: juniorimportance: must knowfreq 62%

basics

~20 s

Permutations count arrangements where order matters; combinations count selections where it does not. A ranked top-3 podium from 30 entrants gives 30 x 29 x 28 = 24,360 outcomes; an unordered shortlist of three gives only 4,060.

open as a page

Why does == fail on two 64-bit float path lengths that are algebraically equal?

level: juniorimportance: must knowfreq 88%

basics

~20 s

Binary floating-point cannot hold most decimal fractions exactly, so every operation rounds. Two totals summed in different orders accumulate different rounding error and end up a few bits apart even when the algebra says they match. Compare with a tolerance.

open as a page

Why does a 32-bit signed revenue total silently go negative instead of raising an error?

level: juniorimportance: must knowfreq 75%

basics

~20 s

Fixed-width integer arithmetic wraps: once a running total passes the largest representable value it continues from the most negative one. Nothing checks for this by default, so the sum is simply wrong from that point on.

open as a page

In Euclid's algorithm, why does replacing (a, b) with (b, a mod b) leave the gcd unchanged?

level: juniorimportance: must knowfreq 78%

basics

~20 s

Every common divisor of a and b also divides a mod b, and every common divisor of b and a mod b also divides a. The two pairs therefore have exactly the same common divisors, and so the same greatest one.

open as a page

Why can a remainder like (dayIndex + delta) % 7 come out negative, and how do you normalize it?

level: juniorimportance: must knowfreq 60%

basics

~20 s

In truncated-division languages the remainder carries the sign of the dividend, so a day offset that lands left of zero yields a negative result. Normalize with ((x % 7) + 7) % 7 to map any integer into 0..6.

open as a page

Why does trial division only need to test divisors up to the square root of n?

level: juniorimportance: must knowfreq 78%

basics

~20 s

Divisors come in pairs whose product is n, so any divisor larger than the square root of n is paired with one smaller than it. Testing up to the square root therefore finds a factor whenever one exists.

open as a page

Why does XOR-ing every value in a stream cancel out the values that appear twice?

level: juniorimportance: must knowfreq 78%

basics

~20 s

XOR is self-inverse (x xor x = 0), has identity 0, and is commutative and associative. So the fold can be reordered to put duplicates side by side; every pair collapses to 0 and only the unpaired value survives.

open as a page

Why does counting an integer from 0 to 2^n - 1 enumerate every subset of an n-element set?

level: middleimportance: must knowfreq 58%

basics

~20 s

Every n-bit integer is a distinct in-or-out choice for each of the n elements, and counting from 0 to 2^n - 1 visits every n-bit pattern exactly once. The number itself is the subset; no list is built.

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

Why does an arithmetic right shift of a negative pixel delta not match integer division by two?

level: middleimportance: must knowfreq 58%

basics

~20 s

An arithmetic right shift rounds toward negative infinity; truncating integer division rounds toward zero. They agree on non-negative values and when the division is exact, but for -7 the shift gives -4 and the division gives -3.

open as a page

When does an absolute epsilon comparison of two 64-bit floats fail, and what replaces it?

level: middleimportance: must knowfreq 66%

basics

~20 s

A fixed absolute epsilon only works in a narrow magnitude band: near 1e12 adjacent 64-bit floats are further apart than 1e-9, while near zero the same epsilon calls wildly different values equal. Use a relative tolerance with an absolute floor.

open as a page

Why does widening the result of a 32-bit multiply to 64 bits happen too late to prevent overflow?

level: middleimportance: must knowfreq 58%

basics

~20 s

Operand width, not destination width, decides where arithmetic happens. A product of two 32-bit values is formed in 32 bits and wraps there; widening afterwards faithfully copies the already-wrong result. Widen at least one operand before multiplying.

open as a page

In a counter accumulating a product mod 1e9+7, why reduce after every multiply instead of at the end?

level: middleimportance: must knowfreq 55%

basics

~20 s

Reducing every step and reducing once at the end give the same answer but not the same intermediates. With both factors below the modulus every product fits a fixed-width accumulator; deferring reduction lets the running value overflow silently.

open as a page

In the Sieve of Eratosthenes, why does crossing out multiples of p start at p squared?

level: middleimportance: must knowfreq 66%

basics

~20 s

Every multiple of p below pp has a prime factor smaller than p, so it was already crossed out when that smaller prime was processed. Starting at pp skips guaranteed-redundant work without ever missing a composite.

open as a page

How does XOR find the one dropped sequence number when a receiver logs n-1 of 1..n?

level: middleimportance: must knowfreq 62%

basics

~20 s

Fold the expected numbers 1..n and the received ones into a single XOR accumulator. Every delivered number then appears exactly twice and cancels, so the accumulator ends holding the one sequence number that never arrived.

open as a page

Why does the single byte 0xC8 decode as 200 when read as unsigned but as -56 when read as signed two's complement?

level: middleimportance: should knowfreq 40%

basics

~20 s

The bits never change — the interpretation does. Unsigned reading weights the top bit +128, so 11001000 is 200. Two's complement weights the same bit -128, giving -128 + 64 + 8 = -56. Signedness is a property of the reading, not the byte.

open as a page

What is sign extension, and what goes wrong when a negative 16-bit value is widened to 32 bits by zero-filling?

level: middleimportance: should knowfreq 45%

basics

~20 s

Sign extension copies the sign bit into every new high-order bit when widening, which preserves the two's-complement value. Zero-filling instead reinterprets a negative as a large positive: the 16-bit value -200 arrives in 32 bits as 65336.

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

What does flags & ACK_MASK == ACK_MASK test when & binds more loosely than ==?

level: middleimportance: should knowfreq 45%

basics

~20 s

It tests the lowest bit of flags, not the ACK bit. The comparison binds tighter, runs first and yields 1, so the line reduces to flags AND 1 — a silent bug that still compiles.

open as a page

Why compute nCr with the multiplicative formula instead of n!/(k!(n-k)!)?

level: middleimportance: should knowfreq 46%

basics

~20 s

The factorials overflow long before the answer does. Choosing 6 winners from 30 entrants is only 593,775, yet 21! already exceeds a 64-bit signed integer. The multiplicative form interleaves multiplying and dividing, keeping every intermediate value near the answer's size.

open as a page

How do NaN and Infinity readings break a max-finding scan over a sensor stream?

level: middleimportance: should knowfreq 47%

basics

~20 s

Every comparison involving NaN is false, so a NaN seed pins the running maximum at NaN forever, while a NaN arriving later is silently skipped. Infinity compares as an ordinary value and wins every comparison. Neither is reported as an error.

open as a page

Why can taking the absolute value of a fixed-width signed integer return a negative number?

level: middleimportance: should knowfreq 38%

basics

~20 s

Signed fixed-width ranges are asymmetric — there is one more negative value than positive. Negating the most negative value has no representable result, so it wraps back to itself, and absolute value hands you that same negative number.

open as a page

Why does Euclid's algorithm need only O(log min(a, b)) steps rather than roughly a/b?

level: middleimportance: should knowfreq 50%

basics

~20 s

One remainder step collapses a whole run of subtractions. After the first swap each remainder is strictly less than half the dividend it came from, so the values shrink geometrically and the step count is logarithmic, not proportional to a/b.

open as a page

Why compute lcm as a / gcd(a, b) * b instead of a * b / gcd(a, b)?

level: middleimportance: should knowfreq 56%

basics

~20 s

Both forms are mathematically equal, but the product a * b can overflow a fixed-width integer before the division ever runs. Dividing first is exact, because the gcd divides a, and keeps every intermediate no larger than the answer.

open as a page

Why can you reduce mod m after each add and multiply in a running checksum, but not after a divide?

level: middleimportance: should knowfreq 45%

basics

~20 s

Congruence survives addition, subtraction and multiplication, so reducing after each of those is exact. Division is not an operation on residues: it needs a modular inverse, which exists only when the divisor shares no factor with the modulus.

open as a page

How do you count all divisors of a number from its prime factorization?

level: middleimportance: should knowfreq 44%

basics

~20 s

Factor the number into primes, then multiply every exponent plus one. A value equal to 2^3 * 3^1 has (3+1)(1+1) = 8 divisors, because a divisor picks each prime's exponent independently, anywhere from zero up to the one available.

open as a page

showing 1–30 of 47