关注我的公众号c137Lab获取更多相关内容
非传统定点数(一)
有符号数(Signed Digit Number,SD)
SD 和 SM(Signed-Magnitude)不同,具有三重值 { 0 , 1 , − 1 } \{0,1,-1\} { 0,1,−1} 其中 -1 也常写成 1 ‾ \overline{1} 1 .已经证明SD数应用在不用进位的加法器和乘法器中能够降低复杂性。
SD码编码示例:
1 4 10 = 1 6 10 − 2 10 = 100 1 ‾ 0 14_{10}=16_{10}-2_{10}=100\overline10 1410=1610−210=100101 4 10 = 8 10 + 4 10 + 2 10 = 01110 14_{10}=8_{10}+4_{10}+2_{10}=01110 1410=810+410+210=01110
可以看出SD码是不唯一的。事实上,要降低乘法的工作量,需要降低运算中非零元素的数量。我们称拥有最少非零元素的SD码为CSD码。
最佳CSD编码
最佳CSD编码:
- 从最低有效位开始,用 10 ⋯ 0 1 ‾ 10\cdots0\overline1 10⋯01 取代所有大于 2 的 1 序列。此外,还需要用 110 1 ‾ 110\overline1 1101 取代 1011 1011 1011
- 从最高位开始,用 011 011 011 代替 10 1 ‾ 10\overline1 101
该方法给出一个非零元素最少的SD码,且其中的减法次数最少。
示例:
2 7 10 = 1101 1 C S D = 1110 1 ‾ C S D = 10 1 ‾ 0 1 ‾ C S D 27_{10}=11011_{CSD}=1110\overline1_{CSD}=10\overline10\overline1_{CSD} 2710=11011CSD=11101CSD=10101CSD
虽然第一步没有减少非零元素的个数,但是它使用加法替代了减法。最终,这一算法将 3 次加法简化为了两次减法。
观察 01 1 2 011_2 0112 ,如果将其转换为 10 1 ‾ 10\overline1 101 ,那么实际上是复杂化了运算,将一次加法变成了一次减法。这也就是最佳CSD编码第二步操作的意义。
分数CSD编码
使用整型数实现分数会导致很大的误差,使用CSD编码可以降低这一误差。
例如 y = 7 x / 8 y=7x/8 y=7x/8,可以写成以下几种实现方式:
y 0 = ( 7 x ) / 8 y0=(7x)/8 y0=(7x)/8y 1 = ( x / 8 ) ∗ 7 y1=(x/8)*7 y1=(x/8)∗7
y 2 = ( ( 4 + 2 + 1 ) x ) / 8 = ( x / 2 ) + ( x / 4 ) + ( x / 8 ) y2=((4+2+1)x)/8=(x/2)+(x/4)+(x/8) y2=((4+2+1)x)/8=(x/2)+(x/4)+(x/8)
y 3 = ( ( 8 − 1 ) x ) / 8 = x − ( x / 8 ) y3=((8-1)x)/8=x-(x/8) y3=((8−1)x)/8=x−(x/8)
不难看出, y 2 y2 y2 是普通二进制码, y 3 y3 y3 是CSD码
使用Verilog实现上述代码并观察结果(iverilog):
module csd( input [4:0] x0, output [4:0] y0, output [4:0] y1, output [4:0] y2, output [4:0] y3 ); assign y0 = 7*(x0)/8; assign y1 = (x0/8)*7; assign y2 = x0/2+x0/4+x0/8; assign y3 = x0-x0/8; endmodule
`timescale 1ns/100ps module csd_tb; reg [4:0] x0; wire [4:0] y0; wire [4:0] y1; wire [4:0] y2; wire [4:0] y3; /*iverilog */ initial begin $dumpfile("wave.vcd"); //生成的vcd文件名称 $dumpvars(0, csd_tb); //tb模块名称 end /*iverilog */ initial begin x0 = 1; #10 x0 = 3; #20 x0 = 6; #30 x0 = 9; #100 $stop; end csd cds_ut0 ( .x0(x0), .y0(y0), .y1(y1), .y2(y2), .y3(y3) ); endmodule
输出结果如下:
可以看出CSD码有效的降低了量化误差。因为实际上除以2的幂的除法是通过移位实现的,误差最大的 y 1 y1 y1 实际上在第一步就已经损失了低3位。
自由进位加法器
SD编码可以实现自由进位加法,通过LUT进行计算,表如下:
实现上表需要一个 2 8 × 4 2^8\times4 28×4 的LUT,计算出 u k u_k uk 和 c k c_k ck 后将 c k c_k ck 左移一位与 u k u_k uk 相加即可得到结果。
乘法-加法器图(Multiplier Adder Graph,MAG)
在最优CSD意义中,经常是先将系数分解成几个因子,再实现具体因子效率比较高。
例如系数93的实现方式可以有以下几种:
93 = 3 × 31 = ( 1 + 2 ) × ( 32 − 1 ) 93=3\times31=(1+2)\times(32-1) 93=3×31=(1+2)×(32−1)93 = 64 + 32 − 4 + 1 93=64+32-4+1 93=64+32−4+1
用流程图表示如下:
对数系统(Logarithmic Number System,LNS)
对数系统与固定尾数和分数指数构成的浮点数制类似,使用如下方式表示:
x = ± r ± e x x=\pm r^{\pm e_x} x=±r±ex
其中 r r r 是数制的基数, e x e_x ex 是对数数制的指数。
举例说明其在计算机中的格式:
假设一个基数为 2 的 9 位 LNS 数,其构成为两个符号位,三位整数精度和四位分数精度:00 011.0010,格式如下:
符号 S x S_x Sx 指数符号 S e S_e Se 指数整数位 I I I 指数分数位 F F F 0 1 100 1110 其十进制表达式为:
2 − 3 − 1 / 8 = 2 − 3.125 2^{-3-1/8}=2^{-3.125} 2−3−1/8=2−3.125
注意到指数部分使用补码表示。
9 位 LNS 能表示的最大数是 2 8 − 1 / 16 ≈ 256 2^{8-1/16}\approx256 28−1/16≈256,最小数是 2 − 8 = 0.0039 2^{-8}=0.0039 2−8=0.0039,和传统定点数相比 LNS 在数字较小的时候采样率更高,更加精细。
历史上,LNS 的优势在于能够有效实现乘法、除法、求平方根或平方,因其可以将以上运算转换为加法、除法和乘法。但是加法和减法的复杂度会增加,加减法使用如下方式进行:
假设 A > B A>B A>B
KaTeX parse error: No such environment: align at position 8: \begin{̲a̲l̲i̲g̲n̲}̲ C&=A+B=2^{e_a}…
可以得到 e c = e a + l o g 2 ( ϕ + ( Δ ) ) e_c=e^a+log_2(\phi^+(\Delta)) ec=ea+log2(ϕ+(Δ))。同理,对于减法有 ϕ − ( Δ ) = 1 − 2 e b − e a \phi^-(\Delta)=1-2^{e_b-e_a} ϕ−(Δ)=1−2eb−ea, e c = e a + l o g 2 ( ϕ − ( Δ ) ) e^c=e^a+log_2(\phi^-(\Delta)) ec=ea+log2(ϕ−(Δ))
参考文献:Digital Signal Processing with Field Programmable Gate Arrays --U.Meyer-Baese