skip to content

questions

4

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

level: juniorimportance: must knowfreq 75%

answer

  1. count the total bit patterns first
  2. where does zero live, and how many zeros are there?
  3. the top bit carries a negative weight, not a flag
  4. split: top bit set vs clear, 128 each
  5. try ~n + 1 on the lowest value

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.

solid answer

~40 s

Eight bits give exactly 256 patterns. Two's complement assigns the most significant bit a weight of -128 and the rest their usual positive weights, so every pattern with the top bit set is negative — that's 128 values, -128 through -1 — and every pattern with it clear is non-negative — 128 values, 0 through 127. Zero occupies one slot on the non-negative side, which is what makes the range asymmetric. Negation is `~n + 1`: flip every bit, add one. Apply it to -128 (`10000000`): flipping gives `01111111`, adding one gives `10000000` — the same pattern back, because +128 would need a ninth bit. Unlike sign-magnitude or one's complement, two's complement has a single zero and lets one adder circuit serve signed and unsigned arithmetic alike, which is why it won.

go deeper

for a junior

Be ready to state the n-bit range, explain that the top bit weighs -2^(n-1), and negate a small number by flipping bits and adding one on the whiteboard without hesitation.

for a middle

An interviewer expects the full mechanism: why zero's placement creates the asymmetry, why ~n + 1 works algebraically, and what pattern the minimum value's negation lands on and why.

for a senior

Connect representation to consequences: recognize when a computation can reach the unpairable minimum value, and explain why sanity checks on widths and ranges belong at the boundaries where binary data enters the system.

for a principal

Own the framing when this bites a design: fixed-width integer semantics are a contract across languages, wire formats, and hardware, and you should be able to justify width and signedness choices in a data format review from these first principles.

## Start by counting patterns An 8-bit value has exactly 2^8 = 256 distinct bit patterns — that budget is fixed before any interpretation is chosen. Read as **unsigned**, the patterns cover 0..255 with the usual place values: bit *i* is worth 2^i. A **signed** encoding must spend the same 256 patterns to cover both signs somehow. ## What two's complement actually is Two's complement is not "a sign flag plus a magnitude". It is a weighting scheme: in an n-bit number, the most significant bit carries weight **-2^(n-1)** and every other bit keeps its positive weight. For 8 bits: ``` value = -128*b7 + 64*b6 + 32*b5 + 16*b4 + 8*b3 + 4*b2 + 2*b1 + 1*b0 ``` So `11111011` is -128 + 64 + 32 + 16 + 8 + 2 + 1 = **-5**, and `10000000` is exactly **-128**. From this one rule everything follows: - Every pattern with the top bit **set** is negative — 128 patterns covering -128..-1. - Every pattern with the top bit **clear** is non-negative — 128 patterns covering 0..127. - Zero (`00000000`) is unique and sits on the non-negative side, which is why there are 128 negatives but only 127 positives. The asymmetry is arithmetic, not a design accident. ## Negation: ~n + 1, and why it works For any n, `n + ~n` sets every bit (each bit position has exactly one 1 between them), and the all-ones pattern is -1. So `~n = -n - 1`, and therefore `~n + 1 = -n`. Walkthrough for 5: ``` 5 = 00000101 ~5 = 11111010 (this is -6) ~5 + 1 = 11111011 = -5 ``` Now apply it to the minimum value: ``` -128 = 10000000 ~(-128) = 01111111 (= 127) +1 = 10000000 (the pattern for -128 again) ``` The negation lands on the same pattern, because the true answer, +128, does not fit in 8 bits — it would need 9. This is the **representational** statement of the famous edge case: the minimum signed value simply has no positive counterpart at the same width. (What a running program does when it evaluates that negation — wrapping, trapping — is a separate overflow topic.) ## Why this encoding won Two other historical encodings spent the 256 patterns differently. **Sign-magnitude** uses the top bit as a pure flag over a 7-bit magnitude, giving -127..+127 — but with two zeros (+0 and -0) and hardware that must special-case subtraction. **One's complement** negates by flipping all bits, also yielding two zeros and an awkward "end-around carry" in addition. Two's complement has a **single zero**, and — decisively — the **same binary adder** produces correct results whether the operands are read as signed or unsigned, because the encoding is arithmetic modulo 2^n. Subtraction becomes "add the negation". That hardware economy is why essentially every mainstream platform committed to it: C++20 made two's complement the only permitted representation after decades of theoretical latitude, and Java, Go, and Rust define their fixed-width integer types as two's complement outright. ## Generalizing the range An n-bit two's-complement integer spans **-2^(n-1) .. 2^(n-1) - 1**: | Width | Minimum | Maximum | |-------|---------|---------| | 8-bit | -128 | 127 | | 16-bit | -32,768 | 32,767 | | 32-bit | -2,147,483,648 | 2,147,483,647 | | 64-bit | -2^63 | 2^63 - 1 | At every width the same facts hold: one more negative than positives, a unique zero, and a minimum value whose negation is not representable. Interviewers probe this because every bit trick, mask, and overflow question downstream assumes you can read a pattern both ways and negate by `~n + 1` without hesitation.

  • Walk me through why ~n + 1 negates a number.
    For any pattern n, `n + ~n` has a 1 in every position, and the all-ones pattern equals -1 in two's complement. So `~n = -n - 1`, and adding 1 gives `~n + 1 = -n`. Concretely: 5 is `00000101`, flipping gives `11111010` (-6), adding one gives `11111011`, which is -128 + 123 = -5.
  • What does ~n + 1 produce when n is already the minimum value?
    The same pattern back. `10000000` flips to `01111111` (127), and adding one returns `10000000`. The mathematically correct answer, +2^(n-1), needs one more bit than the width provides, so no correct representation exists at that width — the negation has no answer, not a wrong answer.
  • Why did two's complement beat sign-magnitude and one's complement in hardware?
    It has a single zero, while both rivals waste a pattern on -0 and force equality checks to treat two patterns as equal. More decisively, two's complement is arithmetic modulo 2^n, so one plain binary adder computes correct signed and unsigned sums with no special cases, and subtraction reduces to adding the negation.

Think of a clock face: 11 o'clock can equally be read as "one hour before 12". Two's complement reads the upper half of the number circle as negative distances back from the rollover point instead of large positive values.

saying these in an interview costs you the question

  • Describes the top bit as a pure sign flag with the other bits unchanged
  • Claims the range is symmetric, or that there are two zeros
  • Cannot execute ~n + 1 on a concrete pattern
  • Thinks the missing +128 is a compiler or language quirk rather than a property of the encoding
  • Says the asymmetry exists because hardware reserves a pattern for errors

context

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

Why can a backwards array scan using an unsigned index never terminate when its loop guard is `i >= 0`?

level: seniorimportance: nice to knowfreq 30%

basics

~20 s

An unsigned integer cannot hold a negative value, so the guard i >= 0 is always true. When i reaches 0, decrementing wraps it to the maximum unsigned value — the bit pattern of -1 — and the scan runs past the array forever.

open as a page