JOVANA
Explore Library Glossary Getting Started Three Levels Fields How it works Mission
Join the mission
All guides

Overflow, Sign Extension, and Bit Tricks

Two's complement looks elegant — but a fixed number of bits can quietly overflow, and copying a number into a wider register needs care. Here we meet integer overflow, sign extension, and the everyday bitwise and shift tricks that make hardware (and clever code) fast.

A fixed number of bits is a clock that wraps around

In the last guide you saw why two's complement is universal: it gives one zero and lets the same adder do both addition and subtraction. But that elegance hides a catch we now have to face head-on. A register holds a fixed number of bits — say 8, 32, or 64 — and only that many. There is no 'next column' to carry into past the top bit. So the values do not run off to infinity; they wrap around, exactly like a 12-hour clock where 11 + 2 lands on 1, not 13.

For an unsigned integer in n bits, the legal values are 0 up to 2^n minus 1. With 8 bits that is 0 to 255. Add 1 to 255 and the result should be 256 — which needs a 9th bit — but there is no 9th bit, so the answer wraps to 0. For a signed 8-bit value the range is minus 128 to plus 127, and adding 1 to 127 wraps to minus 128. The bits did exactly what the arithmetic rules say; it is our interpretation of those bits that suddenly looks wrong.

Overflow: when the answer no longer fits

We call this wrap-around integer overflow: the true mathematical result is outside the range the bits can represent. The crucial — and often surprising — fact is that overflow is detected differently for signed and unsigned numbers, even though binary addition uses the very same gates for both. The hardware does not know whether you meant the bits as signed or unsigned; it just adds. It computes a few condition flags on the side, and software decides which flag matters.

For unsigned addition, overflow means a carry escaped out of the top bit — the carry flag is set. For signed two's-complement addition, the carry-out is meaningless; instead overflow happens when two operands of the same sign produce a result of the opposite sign (positive plus positive giving a negative, or negative plus negative giving a positive). A neat hardware shortcut: signed overflow occurred exactly when the carry into the top bit differs from the carry out of it. That single XOR feeds the overflow flag.

8-bit signed add:  127 + 1
  0111 1111   (+127)
+ 0000 0001   (  +1)
  ---------
  1000 0000   (-128)  <- sign flipped! signed overflow

  carry into bit7 = 1, carry out of bit7 = 0  ->  1 XOR 0 = OVERFLOW

8-bit unsigned add: 255 + 1
  1111 1111   ( 255)
+ 0000 0001   (   1)
  ---------
1 0000 0000   wraps to 0, carry-out = 1  -> CARRY (unsigned overflow)
The same adder, two stories: signed overflow flips the sign bit; unsigned overflow drops a carry off the top.

Sign extension: widening a number without changing it

Programs constantly copy a narrow value into a wider register — an 8-bit byte into a 32-bit register, or a small immediate operand baked into an instruction into a full 64-bit register. If you simply pad the new high bits with zeros, an unsigned value survives fine. But a signed value would be wrecked: minus 1 stored as 8-bit 1111 1111 would, padded with zeros, become 0000 0000 1111 1111 — which is 255, not minus 1.

The fix is sign extension: copy the sign bit (the top bit) into all the new high positions instead of writing zeros. Take minus 5, which is 1111 1011 in 8 bits; sign-extending to 16 bits copies that leading 1 to give 1111 1111 1111 1011, still minus 5, whereas zero-padding would give 0000 0000 1111 1011, which is 251 — wrong. A positive number such as plus 5 (0000 0101) has a 0 sign bit, so sign-extension just adds zeros and agrees with zero-extension. Real instruction sets give you both flavours: a load-byte-signed sign-extends, while load-byte-unsigned zero-extends, and the load instruction you pick tells the hardware which to do.

Bitwise operations: treating a word as a row of switches

Sometimes you do not want arithmetic at all — you want to poke individual bits. A bitwise operation applies a logic gate to each pair of bits in two words independently, with no carries between columns. AND clears bits (any bit ANDed with 0 becomes 0), OR sets bits (any bit ORed with 1 becomes 1), XOR flips bits (XOR with 1 inverts, XOR with 0 leaves alone), and NOT inverts every bit. These map straight onto the gates you will build the ALU from later in this ladder.

A handful of these become everyday idioms. To test whether bit k is set, AND with a mask that has a single 1 in position k and check if the result is nonzero. To set bit k, OR with that mask; to clear it, AND with the mask's NOT; to toggle it, XOR with the mask. A whole set of boolean flags can ride in one integer, packed and unpacked with masks — which is exactly how hardware status registers and permission bits are stored.

Shifts: cheap multiply, divide, and field surgery

A bit shift slides every bit left or right by some number of positions. Shifting left by k drops the high bits off the top and feeds zeros in at the bottom, which multiplies an unsigned value by 2^k — just as shifting a decimal number left multiplies by ten. Shifting right by k divides by 2^k, but right shifts come in two kinds, and the difference matters exactly as much as sign extension did.

A logical right shift feeds zeros into the top, which is correct for unsigned values. An arithmetic right shift feeds copies of the sign bit into the top instead — the same sign-extension idea — so that dividing a negative number by a power of two stays negative. Picking the wrong one turns minus 8 divided by 2 into a large positive number. (Even arithmetic shift rounds toward negative infinity, not toward zero, so it is not identical to integer division for negatives — one more honest wrinkle.)

Shifts and masks together are the surgeon's tools for packing fields. To pull a 4-bit field out of the middle of a word, shift it down so the field sits at the bottom, then AND with a 4-bit mask to erase everything else. To pack a value into that field, AND it down to size, shift it up into place, and OR it in. This exact dance — shift, mask, combine — is how instructions are encoded and decoded, and how an address is later sliced into pieces for caches and page tables further up this ladder.

Putting the tricks to work

These pieces compose into surprisingly compact patterns. Want to know if a number is even? Test the bottom bit with AND 1. Want the low byte of a word? AND with 0xFF. Want to round an address down to a multiple of 8 for alignment? Clear the low 3 bits with AND of NOT 7. Want a fast 'is this a power of two' check? A power of two has exactly one bit set, so x AND (x minus 1) equals 0 for any nonzero power of two — because subtracting 1 borrows through to flip that single 1 and everything below it.

  1. Decide whether your value is signed or unsigned — the bits alone do not say, and every operation that follows depends on this.
  2. When widening it, sign-extend signed values and zero-extend unsigned ones, so the number's meaning survives the move.
  3. When right-shifting, use an arithmetic shift for signed values and a logical shift for unsigned ones.
  4. After arithmetic, check the carry flag for unsigned overflow or the overflow flag for signed overflow before trusting the result.

None of this is magic — it is the same fixed-width two's complement you already met, viewed through the lens of what actually fits in the bits and how we move bits around. Next we leave the integers behind for fractions: how a binary point and then IEEE 754 floating point let a finite word approximate the reals, and the honest pitfall that those approximations are not exact.