最近在进行一些定点化设计仿真,对于二进制乘法,之前只是粗略的考虑了位数问题,没有细节思考乘法过程。刚刚思考了一下,给出解答。为了方便说明,会附带一些伪Verilog代码,希望读者不要挑毛病。
对于有符号数,通常有三种表示方法:原码、反码、补码。设计运算器时只用补码。
1. 补码
设一个N位二进制数x[N-1: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
3. 符号位扩展:
对一个补码形式有符号数进行符号位扩展,其值不变。举个例子,11000=1000
4. 补码加法
对两个位宽不相等的有符号数进行加法运算,需要进行符号位扩展
如 \(a=5'b10011,b=3'b101\),二者都是补码形式,对应的十进制为\(-13\)和\(-3\). 在二者做加法时,先把位宽较短的\(b\)扩展为5位有符号数,即\(a=5'b10011,b=5'b11101\),然后运用二进制加法,结果为6位。
运算结果按照补码解释,结果为\(-2^5+2^4=-16\),结果正确。这里其实可以注意到,\(10000\)表示的也是\(-16\),运算结果进行了符号位扩展,这样是为了避免溢出。
5. 补码乘法
还拿之前的例子,3位数乘5位数.这里先给出计算过程以及运算方法,随后介绍这么做的原理,不关心原理的同学可以只看计算过程。
抹去溢出的第一个数字,剩余为\(00100111\)看作补码,为\(39\),恰好是\(-13 \times -3 = 39\)
将b的每一位与a相乘,结果为补码形式,扩展符号位到总共8位宽度。最后一行蓝色部分比较特殊,如果b的符号位为1,则将乘之后的结果10011全部取反,然后加一,扩展1位符号位后写在最下边,否则就直接全部写0即可。运算结果截断为(5+3)=8位。我们这里直接截断而不考虑溢出,是因为我们可以通过补码的表示范围判断出,N位有符号数乘以M位有符号数时,结果取N+M位时一定不会溢出。其实取M+N-1位也一定不会溢出(我感觉)。
原理部分:
暂时不想写了.jpg
参考链接:
https://www.zhihu.com/question/309627605
有符号数乘法运算