Two's complement is ordinary binary with one change: the most significant bit has a negative weight. For an N-bit value:
For 4 bits the weights are −8, +4, +2, +1. So 1011 is −8 + 2 + 1 = −5, and 0111 is 4 + 2 + 1 = 7.
Bits | Unsigned | Signed | Bits | Unsigned | Signed |
|---|---|---|---|---|---|
0000 | 0 | 0 | 1000 | 8 | −8 |
0001 | 1 | 1 | 1001 | 9 | −7 |
0010 | 2 | 2 | 1010 | 10 | −6 |
0011 | 3 | 3 | 1011 | 11 | −5 |
0100 | 4 | 4 | 1100 | 12 | −4 |
0101 | 5 | 5 | 1101 | 13 | −3 |
0110 | 6 | 6 | 1110 | 14 | −2 |
0111 | 7 | 7 | 1111 | 15 | −1 |
An N-bit value covers −2ᴺ⁻¹ to 2ᴺ⁻¹ − 1. For 8 bits that is −128 to +127, with one more negative value than positive.
A four-bit result keeps only the low four bits: arithmetic is modulo 16. Signedness does not change that stored result. It changes whether the mathematical answer fits the range assigned to those bits. The two highlighted boundaries therefore mean different things.
For codes 0 through 7, both interpretations agree. For codes 8 through 15, the signed value is the unsigned value minus 16. Replacing the top-bit weight +8 with −8 subtracts exactly 16, which explains the labels on the negative half of the wheel.
0111 unsigned 7, signed +7
+ 0001 unsigned 1, signed +1
------
0 1000 carry out 0; stored result 1000Unsigned 8 fits in four bits, so there is no unsigned overflow. Signed +8 does not fit in −8 through +7: the stored code reads as −8, so there is signed overflow. On the wheel this is the bottom boundary, not the return to code zero.
1111 unsigned 15, signed -1
+ 0001 unsigned 1, signed +1
------
1 0000 carry out 1; stored result 0000Unsigned 15 + 1 = 16 needs five bits, so the four-bit result wraps to zero and carry is set. Signed −1 + 1 = 0 fits exactly, so there is no signed overflow. On the wheel this crosses the top boundary. Carry and signed overflow answer different questions.
Four-bit operation | Stored bits | Unsigned carry | Signed overflow |
|---|---|---|---|
0111 + 0001 | 1000 | 0: unsigned 8 fits | 1: signed +8 does not fit |
1111 + 0001 | 0000 | 1: unsigned 16 does not fit | 0: signed 0 fits |
To negate a value, invert every bit and add 1. The reason: x + ~x is all ones, which is −1, so ~x + 1 is −x.
The payoff is large: the same adder performs signed and unsigned addition. Subtraction just adds the negated operand.
input 1000 signed -8
invert 0111
add one + 0001
-----
result 1000 still signed -8, not representable +8The modular negation rule still works: code 8 is its own additive inverse because 8 + 8 = 16, which is zero modulo 16. But a four-bit signed result cannot represent the mathematical answer +8. This is the extra negative value in the range, not a failure of the invert-and-add-one circuit.
To preserve the mathematical result, sign-extend before negating. Widening the already wrapped result afterward is too late:
Correct: widen the input first
1000 (-8 in 4 bits) -> 11000 (-8 in 5 bits)
invert 11000 -> 00111; add 1 -> 01000 (+8)
Too late: negate in 4 bits, then widen
1000 -> 1000 (-8); sign-extend -> 11000 (-8)If the interface must remain four bits, the specification must instead choose what to do: report overflow, reject the input, or saturate to +7. None of those policies makes exact +8 representable in four signed bits.
module tb_negate;
int errors = 0;
logic [7:0] x;
logic signed [7:0] m = -8'sd128;
logic signed [7:0] neg_m;
logic signed [8:0] wide_m, wide_neg;
initial begin
// invert-and-add-one equals 0 - x for every 8-bit value
for (int i = 0; i < 256; i++) begin
x = i[7:0];
if ((~x + 8'd1) !== (8'd0 - x)) errors++;
end
// the asymmetric corner: -(-128) does not fit in 8 bits
neg_m = -m;
if (neg_m !== -8'sd128) errors++;
wide_m = m; // sign-extend the input before negation
wide_neg = -wide_m;
if (wide_neg !== 9'sd128) errors++;
if (errors == 0) $display("PASS: modular negation holds; widening before negation preserves +128");
else $fatal(1, "FAIL: negation errors=%0d", errors);
$finish;
end
endmodule−1 is 8'hFF (all ones). −128 is 8'h80 (only the top bit set).
The bits obey modulo-16 addition in both cases. The first answer, +8, exceeds the signed maximum +7 but fits unsigned. The second unsigned sum, 16, exceeds unsigned maximum 15, while the signed sum is exactly zero.
Before. Sign-extend 1000 to 11000, then negate to 01000 (+8). Negating first wraps to four-bit 1000 (-8), and widening that result cannot recover the lost mathematical answer.
One adder handles signed and unsigned addition, subtraction reuses the adder by negating the second operand, and there is only one zero (sign-magnitude has +0 and −0). That keeps the datapath small and the comparison logic simple.