实现有符号定点数数的四舍五入截断

上一篇 / 下一篇  2018-11-03 20:04:57

设计数学运算逻辑的时候,我们经常会遇到对定点数的截断,我们知道对于无符号数进行四舍五入截断N,需要先加上2**(N-1)再右移N位。例如,把a为U3.4(这里U代表无符号数,3代表整数位有3位,4代表小数位有4位,总位宽位3+4=7位,类似S3.4代表整数部分有3位小数部分有4位的有符号数,总位宽为1+3+4=8位)截断为U3.0。
assign b[2:0] = (a + 7'd8) >> 4;
但是对于有符号数的四舍五入截断,目前我还没发现有资料进行讨论。以前在我在某家欧美公司工作的时候,他们通常不区分有符号数和无符号数,统一按无符号数的四舍五入方法来截断。这种方法是否正确呢?我写了一段测试代码来验证:
module test;
  reg signed [11:0] data;
  reg signed [7:0]  ref_data;
  reg signed [11:0] temp;
  real r;
  
  initial begin
    repeat (1000) begin
      data = $urandom_range(0, 2047);
      data = data - 1024;
      r = $itor(data);
      r = r / 16;
      ref_data = r;
      temp = data + 8;
      if (ref_data != temp[11:4]) begin
        $display("%0d / 16 = %0d, actual is %0d", data, ref_data, $signed(temp[11:4]));
      end
    end
  end
endmodule
运行这段代码你会发现,对于有些负数,采用无符号数的四舍五入方法计算结果会比标准大1。仔细研究错误全部出在对五的取舍上。例如-856/16=-53.5,按标准方法结果应该是-54,但是如果+0.5之后,恰好变成了-53。这是为什么呢?其实我们仔细研究二的补码就会知道,用补码表示数的范围是不对称的,比如10位有符号整数表示的范围是[-1024,1023],所以对于负数需要再减去一个最低位。记对于负数在右移N位之前需要加的数为2**(N-1)-1。正负数统一起来表示为:
rand(x, n) = (x + 2**(n-1) - x[$bits(x)-1]) >>> n
特别的,当N=1时,
rand(x, 1) = (x + (!x[bits(x)-1])) >>> 1 
测试代码如下:
module test;
  reg signed [11:0] data;
  reg signed [7:0]  ref_data;
  reg signed [11:0] temp;
  real r;
  real ref_sum;
  real act_sum;
  
  initial begin
    repeat (1000) begin
      data = $urandom_range(0, 2047);
      data = data - 1024;
      r = $itor(data);
      r = r / 16;
      ref_data = r;
      temp = data + 8 - data[11];
      if (ref_data != temp[11:4]) begin
        $display("%0d / 16 = %0d, actual is %0d", data, ref_data, $signed(temp[11:4]));
      end
    end
  end
endmodule

TAG:

 

评分:0

我来说两句

显示全部

:loveliness: :handshake :victory: :funk: :time: :kiss: :call: :hug: :lol :'( :Q :L ;P :$ :P :o :@ :D :( :)

Open Toolbar
博评网