2026-09-07
The asker multiplies two 12-bit signed integers and expects a 24-bit product. To scale the result back into a 12-bit range, they intuitively right-shift by 12. But empirically, shifting by 11 gives the correct value. They suspect a "double sign bit" is to blame — and they're exactly right.
Why this is subtly interesting: the confusion sits at the intersection of two's-complement arithmetic and hardware bit-width conventions. Signed multiplication is the classic place where the naive rule "N-bit × N-bit → 2N-bit" hides a redundant bit that trips up nearly every FPGA beginner.
The math: the range of a signed 12-bit number is [-2048, +2047], i.e. [-2^11, 2^11 - 1]. The worst-case product is (-2048) × (-2048) = +2^22, which fits in 23 bits (with a sign bit → 24 bits total). Every other product is strictly smaller. In effect, for signed×signed the "true" magnitude occupies only 2N-1 bits, and bit [2N-1] is a duplicate of the sign bit — except in the one degenerate MIN × MIN case.
What "right shift by 12" actually did: the asker was implicitly treating the product as if it were Q1.11 × Q1.11 = Q2.22. In fixed-point Q-format, that pattern is well known:
Cleaner approach in Verilog:
wire signed [11:0] a, b;
wire signed [23:0] prod = a * b; // both operands MUST be `signed`
wire signed [11:0] result = prod[22:11]; // drop redundant sign bit
Gotchas:
signed. In Verilog, if either operand is unsigned, the multiplier is inferred as unsigned and the sign-extension is wrong. This alone accounts for a huge share of "why is my product weird" bugs.MIN × MIN corner case overflows the 12-bit result (+2^22 shifted right by 11 is +2048, unrepresentable in signed 12-bit). Saturation logic is typical: detect a == 12'h800 && b == 12'h800 and clamp to 12'h7FF.>>>), otherwise negative results become large positives. Slicing with prod[22:11] avoids the issue entirely.1 << 10 before the shift for round-half-up if bias matters (audio/DSP contexts).MIN × MIN — so the correct scale-back shift is N-1, not N.
