Signed, complement và overflow: đọc đúng ý nghĩa của bit

Khoảng 45 phútMức 1/3Bản nháp: một số khẳng định chưa đối chiếu spec

Sau bài này bạn sẽ

  • Giải mã unsigned, sign-magnitude, one’s complement và two’s complement
  • Mở rộng dấu và nhận ra giá trị âm nhỏ nhất
  • Phân biệt carry với signed overflow bằng phản ví dụ

Bit cao có ý nghĩa gì?

Bốn bit 1111 có giá trị unsigned là 15. Cũng mẫu ấy đọc theo two’s complement có giá trị −1. Bộ bit chưa đổi; cách giải mã đã đổi.

Khi debug phép so sánh, mở rộng hay phép cộng, hãy ghi cả độ rộng và signedness. Chỉ nhìn chữ số hex chưa đủ biết phép toán có đúng yêu cầu.

Ba cách biểu diễn số có dấu

Bảng dưới dùng đúng bốn bit. One’s complement đảo từng bit của số dương để biểu diễn số âm tương ứng. Two’s complement đảo rồi cộng 1, chỉ giữ bốn bit.

Biểu diễn Miền bốn bit Mẫu +0 Mẫu −0 Mẫu −3
Sign-magnitude −7 .. +7 0000 1000 1011
One’s complement −7 .. +7 0000 1111 1100
Two’s complement −8 .. +7 0000 không có mẫu riêng 1101

Trong sign-magnitude, ba bit thấp là độ lớn. Không áp dụng cách đọc ấy cho two’s complement: 1101 không phải dấu âm kèm độ lớn 5.

One’s complement khi cộng dùng quy tắc end-around carry để hiệu chỉnh carry ra ngoài; không dùng phép cộng modulo thông thường rồi giả định kết quả đã đúng mọi trường hợp. Bài tập phần cứng phía sau chủ yếu dùng two’s complement.

Đọc two’s complement bằng trọng số âm

Với W bit, bit cao có trọng số -2^(W-1); các bit còn lại có trọng số dương như binary. Vì vậy 1101 là -8+4+1=-3.

Cách tính tương đương: lấy giá trị unsigned u; nếu MSB bằng 1 thì trừ 2^W, nếu MSB bằng 0 thì giữ u. Với bốn bit, 13 trừ 16 bằng −3.

Miền là -2^(W-1) .. 2^(W-1)-1. Do có thêm một giá trị âm, đổi dấu số nhỏ nhất không vừa cùng độ rộng. Đảo và cộng 1 cho 1000 vẫn ra 1000; không thể gọi đó là +8 trong bốn bit signed.

Mở rộng trước khi tính

Zero extension chèn số 0 ở trái và giữ giá trị unsigned. Sign extension lặp MSB và giữ giá trị two’s complement.

Mẫu bốn bit Cách mở rộng lên tám bit Giá trị sau mở rộng
Unsigned 1101 0000_1101 13
Signed 1101 1111_1101 −3
Signed 0101 0000_0101 +5

Cắt từ tám xuống bốn bit chỉ giữ giá trị signed nếu các bit bỏ đi đều là phần mở rộng dấu của kết quả. Ví dụ +8 dạng 0000_1000 không vừa bốn bit signed.

Đừng suy signedness từ tên tín hiệu hay kiểu hiển thị waveform. Với SystemVerilog còn phải xét kiểu toán hạng, cast và độ rộng expression; ví dụ đã chạy chỉ xác nhận những trường hợp cụ thể, quy tắc LRM đầy đủ vẫn chờ đối chiếu.

Carry và signed overflow trả lời hai câu hỏi

Carry cho biết tổng unsigned vượt miền không dấu đang giữ. Signed overflow cho biết tổng toán học vượt miền signed. Cùng một bộ cộng bit có thể gặp một cờ, cả hai hoặc không cờ nào.

Cộng bốn bit Kết quả giữ lại Carry Signed overflow
0111 + 0001 1000 0 1: +7 cộng +1 thành mẫu −8
1111 + 0001 0000 1 0: −1 cộng +1 bằng 0
1000 + 1000 0000 1 1: −8 cộng −8 không vừa
0010 + 0001 0011 0 0

Với phép cộng two’s complement cùng độ rộng, overflow xảy ra khi hai đầu vào cùng dấu nhưng kết quả giữ lại đổi dấu. Hai đầu vào trái dấu không gây signed overflow ở phép cộng này.

Muốn giữ tổng chính xác, mở rộng cả hai toán hạng trước phép cộng rồi giữ thêm một bit. Gán một kết quả đã bị cắt sang biến rộng hơn không lấy lại thông tin đã mất.

Trừ và so sánh theo miền đã chọn

Trong two’s complement, phép trừ có thể viết thành cộng số đối theo modulo 2^W. Với bốn bit, 3 trừ 1 dùng 0011 + 1111; giữ bốn bit ra 0010. Carry ra ngoài không phải kết quả signed cần hiển thị.

So sánh cũng cần cách đọc: 1111 lớn hơn 0001 khi unsigned, nhưng −1 nhỏ hơn +1 khi signed. Trong HDL, cần xác định kiểu và cast nhất quán trước phép so.

Khi làm ALU, đặc tả riêng cờ zero, carry/borrow và signed overflow. Có kiến trúc dùng cờ carry theo quy ước “không mượn” khi trừ; không tự đem quy ước từ một ISA sang ISA khác.

Những lỗi thường gặp

  • Gọi bit cao là dấu rồi đọc các bit thấp như độ lớn của two’s complement.
  • Lấy carry thay signed overflow.
  • Zero-extend một số âm trước khi cộng.
  • Cho rằng giá trị âm nhỏ nhất luôn đổi dấu được trong cùng W bit.
  • Trộn toán hạng signed/unsigned và trông chờ waveform tự cho biết kiểu biểu thức.

Ở phỏng vấn, hãy chọn độ rộng nhỏ rồi đưa phản ví dụ: +7 cộng +1 có overflow nhưng không carry; −1 cộng +1 có carry nhưng không overflow. Hai ví dụ giải thích được sự khác nhau rõ hơn việc chỉ nhớ tên cờ.

Tính tay với bốn bit

  1. Giải mã 1010 theo unsigned, sign-magnitude, one’s complement và two’s complement.
  2. Đổi −5 sang two’s complement bốn bit rồi sign-extend lên tám bit.
  3. Cộng +5 với +4: tổng toán học là gì, mẫu bốn bit là gì, hai cờ có giá trị nào?
  4. Cộng −3 với −2 rồi giải mã kết quả. Có overflow không?
  5. Vì sao lấy trị tuyệt đối −8 cần miền kết quả rộng hơn bốn bit signed?

Kiểm tra

Tự kiểm tra: Signed, complement và overflow: đọc đúng ý nghĩa của bit

  1. Bốn bit 1101 đọc two’s complement có giá trị gì?
  2. Mở rộng signed 1101 từ bốn lên tám bit, giữ giá trị, cho mẫu nào?
  3. Cộng bốn bit 0111 với 0001: carry và signed overflow bằng gì?
  4. One’s complement bốn bit có bao nhiêu mẫu zero?
  5. Đổi dấu −8 trong four-bit two’s complement có giữ được +8 chính xác không?

Bằng chứng kiểm chứng

Expected tính trước chạy: SIGNED CHECK raw=16 ones_zero=2 twos_zero=1 pairs=256 overflow=64 carry=120 differ=112 time_ns=272.

Cùng bit, khác cách hiểu: bù một, bù hai và overflow

Kỳ vọng tính trước chạy: SIGNED CHECK raw=16 ones_zero=2 twos_zero=1 pairs=256 overflow=64 carry=120 differ=112 time_ns=272

tb.sv testbench
`timescale 1ns/1ps// Expected BEFORE execution: ones zero=2, twos zero=1 across 16 raw patterns.// 256 pairs: signed overflow=28+36=64; unsigned carry=120; flags differ=112.module tb_a1_signed;  logic [3:0] raw,a,b,sum_bits;  logic [4:0] unsigned_sum;  bit ov,carry_bit;  int raw_count=0,ones_zeros=0,twos_zeros=0,pairs=0,overflow_count=0,carry_count=0,different=0;  int got_ones,got_twos,expected_ones,expected_twos,ai,bi,exact_sum;  bit expected_overflow,expected_carry;  function automatic int ones_value(input logic [3:0] bits_in);    logic [3:0] magnitude;    if(!bits_in[3])return int'($unsigned(bits_in));    magnitude=~bits_in;return -int'($unsigned(magnitude));  endfunction  initial begin    for(int i=0;i<16;i++)begin      raw=4'(i);#1;raw_count++;      got_ones=ones_value(raw);got_twos=int'($signed(raw));      expected_ones=i<8?i:i-15;expected_twos=i<8?i:i-16;      if(got_ones!=expected_ones||got_twos!=expected_twos||int'($unsigned(raw))!=i)        $fatal(1,"signed decode mismatch raw=%0d",i);      if(got_ones==0)ones_zeros++;if(got_twos==0)twos_zeros++;    end    for(int ia=0;ia<16;ia++)for(int ib=0;ib<16;ib++)begin      a=4'(ia);b=4'(ib);#1;pairs++;      unsigned_sum={1'b0,a}+{1'b0,b};sum_bits=unsigned_sum[3:0];carry_bit=unsigned_sum[4];      ov=(a[3]==b[3])&&(sum_bits[3]!=a[3]);      ai=ia<8?ia:ia-16;bi=ib<8?ib:ib-16;exact_sum=ai+bi;      expected_overflow=exact_sum < -8 || exact_sum > 7;expected_carry=ia+ib>=16;      if(ov!=expected_overflow||carry_bit!=expected_carry)$fatal(1,"overflow/carry mismatch a=%0d b=%0d",ia,ib);      if(ov)overflow_count++;if(carry_bit)carry_count++;if(ov!=carry_bit)different++;    end    if(raw_count!=16||ones_zeros!=2||twos_zeros!=1||pairs!=256||overflow_count!=64||carry_count!=120||different!=112||$time!=272)      $fatal(1,"signed experiment contract");    $display("SIGNED CHECK raw=16 ones_zero=2 twos_zero=1 pairs=256 overflow=64 carry=120 differ=112 time_ns=%0d",$time);    $finish;  endendmodule

Đã kiểm chứng thế nào

  • Phân tích và elaborate theo LRM (pyslang 12.1.0)
  • Mô phỏng, exit 0 và output khớp kỳ vọng tính tay (Verilator 5.050, 2-state)

Output phải khớp: SIGNED CHECK raw=16 ones_zero=2 twos_zero=1 pairs=256 overflow=64 carry=120 differ=112 time_ns=272

Verilator 5.050 two-state; finite known input domains only. No X/Z or all-width language-conformance claim.

Các kiểm tra chạy lúc xây dựng nội dung (2026-11-07), không chạy trong trình duyệt. Kỳ vọng được tính tay trước khi chạy.

Kiểm đủ 16 mẫu đối với unsigned, one’s complement và two’s complement. Tham chiếu dùng số nguyên từ chỉ số mẫu; phép đọc signed trong ví dụ được so với u trừ 16 khi u từ 8 trở lên.

Với 256 cặp cộng, điều kiện overflow theo dấu được so với tổng số nguyên ngoài miền −8 tới +7. Carry được đối chiếu với tổng unsigned từ 16 trở lên. Có 64 cặp overflow, 120 cặp carry và 112 cặp khác cờ. Đây là kiểm chức năng hữu hạn bằng parser/simulation, không claim synthesis hay kiểm toàn bộ LRM/X/Z.

[1] [2] [3] [4]

Nguồn và mức độ đối chiếu

  1. Signed and Unsigned Binary — bản chụp resources.zip, Binary Number Formats; Unsigned Numbers; Signed Numbers; Convert Unsigned Binary to Decimal; Convert Signed Binary to Decimal (Two’s Complement); Alternative Method: Sign-Magnitude (Less Common). Trích dẫn; nguồn đã đọc các mục liên quan.
  2. Thí nghiệm nguyên bản A1.signed, Sixteen raw complement interpretations and 256 overflow/carry addition pairs. Trích dẫn; nguồn đã đọc toàn văn.
  3. IEEE 1800-2023 SystemVerilog LRM, Signed and unsigned integral types, explicit casts, extension and addition result widths — chưa đối chiếu LRM. Chưa đối chiếu với văn bản nguồn (chưa mở).
  4. One's and Two's complements — bản chụp resources.zip, What Are Complements?; One’s Complement; Two’s Complement; Using Two’s Complement for Subtraction; Complement Properties. Trích dẫn; nguồn đã đọc các mục liên quan.
  5. IEEE 1800-2023 SystemVerilog LRM, Signed interpretation, sign extension and fixed-width arithmetic — chưa đối chiếu LRM. Chưa đối chiếu với văn bản nguồn (chưa mở).