Fixed-point: thang đo, làm tròn và xử lý tràn

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

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

  • Tính miền và LSB từ tổng số bit W và số bit phần lẻ F
  • Căn thang đo khi cộng và rescale sau nhân
  • Phân biệt floor, cắt về zero, wrap và saturation

Dấu chấm nhị phân là một quy ước

Tám bit lưu số nguyên 12 có thể đại diện cho 12, 1.5 hoặc 0.046875, tùy hệ số tỉ lệ đã chọn. Fixed-point giữ vị trí dấu chấm nhị phân cố định trong cách diễn giải, không cần thêm một ký tự dấu chấm vào thanh ghi.

Bài này luôn ghi W là tổng số bit, F là số bit phần lẻ và dùng two’s complement. Giá trị thực bằng raw × 2^(-F). Tránh chỉ ghi tên Q-format khi chưa chốt quy ước có tính bit dấu hay không.

Độ rộng quyết định miền và bước lượng tử

Bước nhỏ nhất, hay LSB, là 2^(-F). Miền signed là -2^(W-F-1) .. 2^(W-F-1)-2^(-F).

Với W bằng 4 và F bằng 2, raw signed chạy từ −8 tới 7, nên giá trị chạy từ −2 tới 1.75 theo bước 0.25.

Raw bits Raw signed Giá trị, F bằng 2
1000 −8 −2
1111 −1 −0.25
0000 0 0
0011 3 0.75
0111 7 1.75

Giữ W mà tăng F làm bước nhỏ hơn nhưng miền hẹp hơn. Thêm bit phần lẻ không phải miễn phí: cần xem mức tín hiệu lớn nhất và sai số cho phép.

Cộng sau khi đưa về cùng thang đo

Nếu hai số cùng F, cộng các raw rồi giữ đủ bit cho tổng. Ví dụ 1.5 có raw 6 và 0.75 có raw 3; tổng raw 9 biểu diễn 2.25 khi vẫn dùng F bằng 2. Tổng ấy cần năm bit signed, không vừa bốn bit của định dạng ban đầu.

Nếu F khác nhau, phải căn dấu chấm. Giá trị 0.5 có raw 1 với F bằng 1, hoặc raw 2 với F bằng 2. Cộng raw 1 với raw 3 của số 0.75 sẽ cho kết quả sai thang đo. Đưa 0.5 về raw 2 rồi mới cộng được raw 5, tức 1.25.

Khi tăng F, mở rộng độ rộng trước khi dịch trái để giữ bit có nghĩa. Khi giảm F, những bit bỏ đi tạo ra sai số cần xử lý bằng chính sách đã định.

Nhân làm cộng số bit phần lẻ

Nhân hai raw lần lượt có F_a và F_b bit phần lẻ tạo ra raw tích có F_a+F_b bit phần lẻ. Với hai đầu vào signed rộng W_a và W_b, giữ W_a+W_b bit là cách chứa đầy đủ tích.

Ví dụ 0.75 có raw 3 và 0.5 có raw 2 ở F bằng 2. Tích raw 6 thuộc F bằng 4, nên giá trị là 6/16 bằng 0.375. Nếu hiểu nhầm raw 6 vẫn ở F bằng 2 sẽ đọc thành 1.5.

Muốn trả về F bằng 2 phải bỏ hai bit phần lẻ của tích và quyết định cách làm tròn. Đây là một bước thay đổi độ chính xác, không chỉ là đổi cách in.

Floor khác cắt về zero ở số âm

Bảng dùng tích đã nhân hai đầu vào W bằng 4, F bằng 2. Cần giảm F của tích từ 4 xuống 2, tức chia raw tích cho 4 trước khi chọn kết quả nguyên.

Giá trị tích Raw tích Raw sau floor Raw cắt về zero
+0.375 6 1, tức +0.25 1, tức +0.25
−0.375 −6 −2, tức −0.5 −1, tức −0.25
−0.5 −8 −2, tức −0.5 −2, tức −0.5

Dịch phải số học trên raw signed giữ dấu và tương ứng floor khi chia cho lũy thừa của 2. Phép chia số nguyên có dấu trong ví dụ cắt về zero. Hai cách chỉ khác khi số âm còn phần dư.

Round-to-nearest cũng cần quy tắc khi đúng giữa hai mức, chẳng hạn ties-to-even. “Cộng nửa LSB rồi dịch” không tự diễn đạt mọi chính sách làm tròn, nhất là khi có số âm.

Chọn wrap hay saturation sau rescale

Với đầu ra W bằng 4, F bằng 2, +2.25 nằm ngoài miền. Raw sau rescale là 9: giữ bốn bit thấp thành 1001, đọc signed là −7, tức −1.75. Đó là wrap.

Saturation chặn giá trị dương quá miền ở raw 7, tức +1.75; phía âm chặn ở raw −8, tức −2. Hai chính sách cho hành vi rất khác trong xử lý tín hiệu hoặc điều khiển.

Trình tự cần rõ: giữ tích đủ rộng → rescale/làm tròn → xét miền của kết quả ấy → wrap hoặc saturation theo yêu cầu. Làm tròn có thể đẩy một giá trị sát biên ra ngoài; không bỏ bước xét tràn sau làm tròn.

Ví dụ HDL của bài so cả floor và cắt về zero, nhưng đầu ra wrap/saturation lấy từ nhánh floor. Không ghép kết quả của hai chính sách khác nhau khi đọc bảng.

Những lỗi dễ lọt khi chỉ nhìn bit

  • Không ghi F trong giao diện dữ liệu.
  • Cộng các raw có thang đo khác nhau.
  • Giữ độ rộng của một toán hạng cho tích đầy đủ.
  • Đồng nhất dịch phải số học với cắt về zero cho số âm.
  • Saturate ở miền cũ trước khi rescale, rồi bỏ kiểm tra miền đầu ra.
  • Cắt bit trước phép nhân hoặc trước khi mở rộng, làm mất thông tin không khôi phục được.

Trong review, ghi giá trị thực bên cạnh raw của ít nhất một ca dương, một ca âm và một ca sát biên. Việc này thường làm lộ sai thang đo rất nhanh.

Tính cả raw lẫn giá trị

  1. Với signed W bằng 8, F bằng 3, miền và LSB là gì?
  2. Mã hóa −1.25 trong W bằng 4, F bằng 2.
  3. Nhân 0.75 với −0.5 rồi rescale bằng floor và cắt về zero; so sánh sai số.
  4. Cộng 1.5 với 0.75 rồi đưa về W bằng 4, F bằng 2: wrap và saturation khác nhau ra sao?
  5. Nếu giảm F từ 3 xuống 1, bỏ bao nhiêu bit? Cần kiểm điều gì nếu chọn round-to-nearest?

Kiểm tra

Tự kiểm tra: Fixed-point: thang đo, làm tròn và xử lý tràn

  1. Signed W=4, F=2 có miền và LSB nào?
  2. Nhân hai raw cùng F=2 tạo raw tích đầy đủ có bao nhiêu bit phần lẻ?
  3. Giảm raw −6 bằng chia cho 4: floor và cắt về zero cho kết quả nào?
  4. Raw sau rescale bằng 9, đầu ra signed bốn bit: wrap và saturation là gì?
  5. Raw 1 với F=1 cộng raw 3 với F=2 cần làm gì trước?

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

Expected tính trước chạy: FIXED CHECK pairs=256 inexact=128 floor_trunc_diff=64 overflow=39 time_ns=256.

Fixed-point W=4/F=2: rescale, floor, truncation và saturation

Kỳ vọng tính trước chạy: FIXED CHECK pairs=256 inexact=128 floor_trunc_diff=64 overflow=39 time_ns=256

fixed_rescale.sv RTL
`timescale 1ns/1psmodule fixed_rescale(input logic signed [3:0] a,b,  output wire signed [7:0] full_product,floor_raw,trunc_raw,  output wire signed [3:0] wrapped,saturated,output wire overflow,inexact);  assign full_product=a*b;  assign floor_raw=full_product>>>2;  assign trunc_raw=full_product/8'sd4;  assign wrapped=floor_raw[3:0];  assign saturated=(floor_raw>8'sd7)?4'sd7:(floor_raw < -8'sd8)?4'sh8:floor_raw[3:0];  assign overflow=(floor_raw>8'sd7)||(floor_raw < -8'sd8);  assign inexact=|full_product[1:0];endmodule
tb.sv testbench
`timescale 1ns/1ps// Expected BEFORE execution: signed W4/F2 inputs, exhaustive256 products.// Fractional rescale128; floor/trunc differ64; floor-target overflow21+18=39.module tb_a1_fixed;  logic signed [3:0] a=0,b=0;  wire signed [7:0] full_product,floor_raw,trunc_raw;  wire signed [3:0] wrapped,saturated;  wire overflow,inexact;  int pairs=0,inexact_count=0,rounding_differences=0,overflows=0;  int ai,bi,p,expected_floor,expected_trunc,expected_wrap,expected_sat;  bit expected_overflow,expected_inexact;  fixed_rescale dut(.*);  initial begin    for(int ia=0;ia<16;ia++)for(int ib=0;ib<16;ib++)begin      a=4'(ia);b=4'(ib);#1;pairs++;      ai=ia<8?ia:ia-16;bi=ib<8?ib:ib-16;p=ai*bi;      // Integer reference avoids shifting the DUT's product.      expected_floor=p>=0?p/4:-((-p+3)/4);expected_trunc=p/4;      expected_wrap=((expected_floor%16)+16)%16;if(expected_wrap>=8)expected_wrap-=16;      expected_sat=expected_floor>7?7:expected_floor < -8?-8:expected_floor;      expected_overflow=expected_floor < -8||expected_floor>7;expected_inexact=(p%4)!=0;      if(int'(full_product)!=p||int'(floor_raw)!=expected_floor||int'(trunc_raw)!=expected_trunc||         int'(wrapped)!=expected_wrap||int'(saturated)!=expected_sat||         overflow!=expected_overflow||inexact!=expected_inexact)        $fatal(1,"fixed rescale mismatch a=%0d b=%0d",ai,bi);      if(inexact)inexact_count++;if(floor_raw!=trunc_raw)rounding_differences++;if(overflow)overflows++;    end    if(pairs!=256||inexact_count!=128||rounding_differences!=64||overflows!=39||$time!=256)      $fatal(1,"fixed experiment contract");    $display("FIXED CHECK pairs=256 inexact=128 floor_trunc_diff=64 overflow=39 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)
  • Lint -Wall (Verilator 5.050)
  • Mô phỏng, exit 0 và output khớp kỳ vọng tính tay (Verilator 5.050, 2-state)
  • Tổng hợp, số cell khớp kỳ vọng (Yosys 0.70)

Output phải khớp: FIXED CHECK pairs=256 inexact=128 floor_trunc_diff=64 overflow=39 time_ns=256

Kỳ vọng sau tổng hợp: fixed_rescale: $_*DFF* = 0fixed_rescale: $_*LATCH* = 0

Verilator 5.050 two-state, finite known inputs only; no X/Z or all-width conformance claim.

W=4 total bits and F=2 fractional bits, signed two complement. Floor is arithmetic shift; division truncates toward zero. Generic synthesis only, no physical PPA.

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 đủ 256 cặp raw signed bốn bit. Tham chiếu dùng số nguyên để tính tích, chia floor theo công thức riêng ở phía âm, modulo wrap và clamp saturation. Có 128 cặp còn phần dư, 64 cặp khác giữa floor và cắt về zero; nhánh floor có 39 cặp vượt miền đầu ra.

Parser, lint, simulation và synthesis generic đạt; không claim traced, mapping DSP, STA hay PPA. DUT tổ hợp không có flip-flop/latch. Lượt này chỉ xác nhận đúng định dạng W bằng 4/F bằng 2, chính sách đã nêu và các bit 0/1.

[1] [2] [3]

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

  1. Fixed-Point Arithmetic in Hardware — bản chụp resources.zip, Q-Format: Where the Binary Point Lives; The Designer’s Trade-Off: Range vs. Precision; Addition: Align the Points, Add a Guard Bit; Multiplication: Widths and Points Add; Round, Then Saturate: A Complete Q1.15 Multiplier. Trích dẫn; nguồn đã đọc các mục liên quan.
  2. Thí nghiệm nguyên bản A1.fixed, Exhaustive W=4 F=2 signed products, floor versus truncation, wrap and saturation. Trích dẫn; nguồn đã đọc toàn văn.
  3. IEEE 1800-2023 SystemVerilog LRM, Signed multiplication, arithmetic shifts, integer division and result sizing — chưa đối chiếu LRM. Chưa đối chiếu với văn bản nguồn (chưa mở).