有符号数乘法

最近在进行一些定点化设计仿真,对于二进制乘法,之前只是粗略的考虑了位数问题,没有细节思考乘法过程。刚刚思考了一下,给出解答。为了方便说明,会附带一些伪Verilog代码,希望读者不要挑毛病。
对于有符号数,通常有三种表示方法:原码、反码、补码。设计运算器时只用补码。
1. 补码

设一个N位二进制数x[N-1:0]其有符号形式的物理值(数学运算带入的值)为

\[x=-x[N-1]2^{N-1}+x[N-2]2^{N-2}+...+x[0]2^0 \]

这里唯一需要注意的是,最高位作为符号位,其位权为负,其余为正。
比如,
\(011=-0*2^2+1*2^1+1*2^0=+3\)
\(111=-1*2^2+1*2^1+1*2^0=-1\)

2. 原码

无符号数:

\[x=x[N-1]2^{N-1}+x[N-2]2^{N-2}+...+x[0]2^0 \]

有符号数:
符号位决定正负,后边为那个数的绝对值.比如,-3=111,第一个1代表负号,后边的11为3

\[x=(正或负,由符号位决定)x[N-2]2^{N-2}+...+x[0]2^0 \]

3. 符号位扩展:
对一个补码形式有符号数进行符号位扩展,其值不变。举个例子,11000=1000
4. 补码加法
对两个位宽不相等的有符号数进行加法运算,需要进行符号位扩展
\(a=5'b10011,b=3'b101\),二者都是补码形式,对应的十进制为\(-13\)\(-3\). 在二者做加法时,先把二者都扩展为6位有符号数,即\(a=6'b110011,b=6'b111101\),然后运用二进制加法,结果为6位。

\[\begin{array}{rrrrrr} & 1& 1 & 0 & 0 & 1 & 1 \\ + & 1& 1 & 1 & 1 & 0 & 1 \\ \hline & 1 & 1 & 0 & 0 & 0 & 0 \\ \end{array}\]

运算结果按照补码解释,结果为\(-2^5+2^4=-16\),结果正确。这里其实可以注意到,\(10000\)表示的也是\(-16\),运算结果进行了符号位扩展,这样是为了避免溢出。
5. 补码乘法
还拿之前的例子,3位数乘5位数.这里先给出计算过程以及运算方法,随后介绍这么做的原理,不关心原理的同学可以只看计算过程。

\[\begin{array}{rrrrrrrrr} & & & & 1 & 0 & 0 & 1 & 1 \\ \times & & & & & & 1 & 0 & 1 \\ \hline & \textcolor{red}{1} & \textcolor{red}{1} & \textcolor{red}{1} & 1 & 0 & 0 & 1 & 1 \\ & \textcolor{red}{0} & \textcolor{red}{0} & 0 & 0 & 0 & 0 & 0 & \\ & \textcolor{red}0 & \textcolor{#0f00f0}0 & \textcolor{#0f00f0}1 & \textcolor{#0f00f0}1 & \textcolor{#0f00f0}0 & \textcolor{#0f00f0}1 \\ \hline 1 & 0 &0 &1 &0 &0 &1 &1 &1 \end{array}\]

抹去进位的第一个数字,剩余为\(00100111\)看作补码,为\(39\),恰好是\(-13 \times -3 = 39\)

·计算方法:将b的每一位与a相乘,结果为补码形式,扩展符号位到总共8位宽度。最后一行蓝色部分比较特殊,如果b的符号位为1,则将乘之后的结果10011扩展一位符号位,然后全部取反后加一,写在最下边,否则就直接全部写0即可。运算结果截断为(5+3)=8位,就是最终结果。

  1. 我们这里直接截断而不考虑溢出,是因为我们可以通过补码的表示范围判断出,N位有符号数乘以M位有符号数时,结果取N+M位时一定不会溢出。
  2. 注意最后一行必须先扩展符号位再全部取反加一,原因同样在于补码的表示范围正数比负数少一个,比如5位符号数,-16的相反数是16,但16需要6位才能表示。

·原理:

对于补码,\(2^N*B=\{B,N\{0\}\}\).也就是向左移N位相当于乘2^N.比如,\(11=-1,110=-2,1100=-2*2=-4.\)
对于有符号数乘法,

\[A*B=(-A_{N-1}2^{N-1}+A_{N-2}2^{N-2}+...+A_02^0)*B \]

可以把乘法拆分为N个补码加法运算,竖式的每一行,就是一个加数(称为部分积)。

\[\begin{array}{rrrrrrrrrr} & & & & 1 & 0 & 0 & 1 & 1 & \textcolor{#B0C4DE}{B} \\ \times & & & & & & 1 & 0 & 1 & \textcolor{#B0C4DE}{A}\\ \hline & \textcolor{red}{1} & \textcolor{red}{1} & \textcolor{red}{1} & 1 & 0 & 0 & 1 & 1 & \textcolor{#B0C4DE}{A_02^0B} \\ & \textcolor{red}{0} & \textcolor{red}{0} & 0 & 0 & 0 & 0 & 0 & & \textcolor{#B0C4DE}{A_12^1B}\\ & \textcolor{red}0 & \textcolor{#0f00f0}0 & \textcolor{#0f00f0}1 & \textcolor{#0f00f0}1 & \textcolor{#0f00f0}0 & \textcolor{#0f00f0}1 & & & \textcolor{#B0C4DE}{-A_22^2B}\\ \hline 1 & 0 &0 &1 &0 &0 &1 &1 &1 \end{array}\]

可以看到,前N-1行分别是\(A_{n}2^{n}*B, n=0,1,2...,N-2\).竖式中每一行都左移一位,因为2的幂次在逐次升高。对于最后一行,部分积是\(-A_{N-1}2^{N-1}*B\).因此在计算得出\(A_{N-1}2^{N-1}*B\)后,需要取相反数。如果A是负数,那么\(A_{N-1}=1\),部分积不为0,补码的相反数就是各位按位取反再加1;如果A是正数,那么其符号位\(A_{N-1}=0\),部分积是0,取相反数仍然是0,补码的0只有一种表示方法,不需要改动。这就是乘法的竖式中蓝色部分来源。求出全部的部分积后进行求和,由于不同行的位宽不同,因此需要扩展符号位到统一的宽度,也就是竖式中的红色部分,然后进行加法运算,得到最终结果。
参考链接:
https://www.zhihu.com/question/309627605
有符号数乘法运算

posted @ 2023-07-24 16:10  蕉太羊  阅读(806)  评论(0编辑  收藏  举报