EETOP 创芯网论坛 (原名:电子顶级开发网)

标题: 关于补码的加法和乘法运算 [打印本页]

作者: larlyii    时间: 2011-5-31 21:44
标题: 关于补码的加法和乘法运算
本帖最后由 larlyii 于 2011-5-31 22:14 编辑

小弟我最近被一个问题纠结好久,就是有符号,无符号数,原码补码之间的运算,比如举个例吧,-6和7,我去算它们之间的加法和乘法。
假设输入数据用8位2进制表示。

首先,-6的原码是10000110,补码是11111010;7的原码是00000111,补码同样(因为是正数)
加法:加出来有9位:100000001,如果我们只取低8位,就是00000001,为十进制的1,答案正确。但是好多书上写的代码都是把加法器的输出扩展了一位的,那就是说,这个数的十进制表示变成了-255,明显不对~

乘法:乘出来有11位:11011010110,直接取的话肯定不对,但是如果只取低8位的话是11010110(补码),即十进制的-42,就正确。但是一般为了避免溢出,我看好多书上都是把乘法器的输出位宽扩展成输入的2倍,那这样输出又和上面的加法同样的由于扩展位宽而出现的错误。

按照以上逻辑,岂不是我设计的模块的输出必须和输入有同样的位宽?但实际情况显然不是,不知这是怎么一回事?

现贴上ModelSim的图为证:
( , 下载次数: 261 ) 十进制的
( , 下载次数: 251 ) 二进制的
作者: l7951247    时间: 2011-5-31 22:45
教你一个方法,一个三位和一个4位的有符号数相加,将二者都变成5位的,就是将二者的高位复制,然后相加后结果去掉最高一位,就是结果
作者: JD.WARLOCK    时间: 2011-5-31 22:46
有符号数的加法无视最高位(就是符号位)的溢出,即11111010+00000111=00000001。
   无符号数的加法,需要扩展以为,因为最高位可能溢出。
   乘法的话,应该是先对2个数的符号位异或,异或的结果作为输出结果的符号位,然后再将乘数和被乘数都正值化,相乘的结果再加上符号位,就是最终的结果了
作者: larlyii    时间: 2011-6-1 07:57
本帖最后由 larlyii 于 2011-6-1 08:06 编辑

回复 2# l7951247


    这个原理我知道,但按照这样的方法,我在用FPGA实现的2个数相加时,岂不是就要先把输入的数扩展1位,再通过加法器,然后再把最高位去掉?但我看一般的书上直接就把2个数相加了呀,没有在之前扩展1位。

我主要就是纠结用FPGA实现算术运算和原码补码理论的算术运算之间的联系和差异。因为FPGA根本不知道输入的数是原码或补码,它会直接按照无符号数的加法乘法去操作的。
作者: larlyii    时间: 2011-6-1 08:04
本帖最后由 larlyii 于 2011-6-1 08:07 编辑

回复 3# JD.WARLOCK


    你说的加法我懂了,但乘法,你的意思是原码相乘吧?就是符号位和数据位分别处理,所以我觉得原码的乘法更好操作,但是如果我本生FPGA输入的数据就是补码的话,我是不是要先变成原码,然后再按照你说的方法那样去做?但我看很多书上都直接就对输入的2个数相乘就完了,它也没说这个输入输出是补码原码,所以一直比较混乱。


通过仿真我推断出FPGA根本不知道输入的数是原码或补码,它会直接按照无符号数的加法乘法去操作的。
作者: justshuashua    时间: 2011-6-1 09:07
如果想直接写加法进行有符号数的相加,则需要定义成有符号数类型,例如wire signed  [7:0]  a,b,c;
assign c=a+b;即使位宽不匹配也可以不用关心,综合工具会自动匹配;
如果定义成无符号数,即95语法的,则对有符号数的计算需要手动扩展符号位(即最高位,而不是扩展0)后再进行相加。初学者建议采用手动扩展符号位的方式实现。
作者: dlb05061131    时间: 2011-6-1 09:39
FPGA内的符号运算,你需要将你的数变成补码再进行运算。
对于两个八位相加,它是先将两个加数扩展为九位再相加。

我曾经也为这个纠结很久。
给你个文档。
作者: AmoiBB    时间: 2011-6-1 09:52


   
回复  l7951247


    这个原理我知道,但按照这样的方法,我在用FPGA实现的2个数相加时,岂不是就要先 ...
larlyii 发表于 2011-6-1 07:57



我觉得FPGA内的加法器或者乘法器不需要知道输入是原码还是补码,但是你必须要清楚,然后你根据输入是原码还是补码来决定将加法器或者乘法器输出中的哪些位截取出来作为最终的结果,不知道说的清楚不
作者: dlb05061131    时间: 2011-6-1 09:52
上边这个文档里边说到 第一个例子里边就讲了  比如你的-6 +7 。先扩展为9位,然后进行相加,结果也是取九位,第十位溢出就不要了。就是结果。
作者: larlyii    时间: 2011-6-1 21:34
回复 6# justshuashua


    按照你的说法,比如我2个8位的有符号数相加,我需要先把它扩展成9位,然后如果加出来的数仍然是9位,就把这9位作为输出,如果加出来的数是10位,我就把最高位去掉,仍然是9位输出。
即:


   

        

                
  1. input [7:0]a, b;
  2. output [8:0]sum;
  3. assign a_extend = {a[7], a};
  4. assign b_extend = {b[7], b};
  5. assign sum = a_extend + b_extend;

  6.         

   

    复制代码


这样对吧??
作者: larlyii    时间: 2011-6-1 21:35
回复 7# dlb05061131


    谢谢你的资料哈!超级感谢!我留着慢慢看
作者: larlyii    时间: 2011-6-1 21:40
回复 8# AmoiBB


    加法器确实不知道输入的是源码还是补码,但是我验证过,当把乘法器设置成unsigned时,它把输入当做无符号数,设置成signed时当做有符号数。当对有符号数运算时,设置成signed就是对的
作者: 110500623    时间: 2011-6-7 09:18
按照我的经验:补码直接相加减,不用高位扩展,结果溢出的最高位也可以直接不用。
作者: njithjw    时间: 2011-6-7 21:31
楼主最后的代码是正确的
作者: guoyu    时间: 2011-6-10 08:28
不要人工去枚举符号位,用signed来表明你设计的思路。必要时可用cast函数:$signed(),$unsigned.
作者: wangxuede220    时间: 2011-6-10 08:36
本帖最后由 wangxuede220 于 2011-6-10 08:37 编辑

加法的说法13楼是正确的,楼主你乘法时之所以只取低8位正确,因为你的结果没有超出8位,而乘法有可能超出8位的,这个时候再像你这么截断就不对了。你或者可以设计一个补码乘法器,但是结果一定要保持8+8-1的宽度。如果直接用“*”的话,也是15位,最高位是符号位相异或的结果,其他低14是两个低七位原码乘法,乘完之后再转化成为补码。
作者: sonicsun    时间: 2011-6-10 10:35
变量声明的时候直接声明 signed 有符号数,然后考虑到溢出的情况就成…

不用那么麻烦…仿真软件会自动支持…综合软件也会帮你综合好…
作者: violet701    时间: 2011-7-14 12:21
我也碰到问题了 可能就是这个问题
作者: hahaer    时间: 2011-7-14 16:38
回复 10# larlyii


    实际上,你根本不需要考虑去掉高位,输入进行有符号扩展,输出和输入位宽一致即可。
作者: scxm    时间: 2011-7-15 00:19
学习了!!
作者: bank2003    时间: 2012-5-4 03:36
了解了~~~
作者: dearhero    时间: 2012-5-4 15:47
学习了!
作者: youfengwulei    时间: 2012-5-25 12:59
感觉这个补码的乘法还是没讲解清楚呀,我现在也遇到这个问题了。纠结中。。。
作者: zhongjee    时间: 2012-5-26 19:04
补码可以直接相加减,要做乘法必须先换成原码,乘法之后的位宽是连个乘数的位宽之和就可以了
作者: yucaoxilin    时间: 2013-8-22 09:52
我觉得补码加法之所以扩展一位,是为了防止溢出……两个相同符号的补码相加,很容易溢出……
作者: yucaoxilin    时间: 2013-8-22 10:24
刚才随便用Quartus仿真了下补码的乘法,加上use IEEE.STD_LOGIC_SIGNED.ALL会默认为有符号数(补码),自动调用乘法器进行补码乘法~
作者: midle    时间: 2014-5-9 22:55
回复 26# yucaoxilin


   这句话加在约束文件中吗?
作者: zhang0107yue    时间: 2014-5-15 19:23
xuexixuexi
作者: hutiantian    时间: 2014-6-17 22:31
这个问题好纠结
作者: 727518751    时间: 2014-6-22 15:48


   
补码可以直接相加减,要做乘法必须先换成原码,乘法之后的位宽是连个乘数的位宽之和就可以了
zhongjee 发表于 2012-5-26 19:04



不对。乘法可以用booth,不用换成原码。
作者: matafeiye    时间: 2014-7-7 21:34
谢谢分享
作者: qiufenglinyue9    时间: 2017-12-27 11:05
不用考虑那么多,标明signed即可,现在的设计越来越往行为上,这些细节综合已经优化,考虑溢出即可。
作者: glace12123    时间: 2018-1-5 21:37
你对有基2补码的理解是错误的。。。。2个N位的有符号数相加,必须各自扩展符号到MSB上形成2个N+1位数相加,最后得到的和取低N+1位,作为和输出,这时和就是N+1位,最高位为符号位。。
所谓乘法,也就是加法的扩展版而已。。。。、
至于其原理,可以从高等代数扯到计算机原理。。。。总之,有符号数,分为1bit符号位和N bit数据位,而且数位能表达的范围比无符号数少了一半(少了1bit等于除以2嘛),如果是2个正数或者2个负数相加,其结果是有可能比原来能表达的范围大的,如果还是用原来的N bit去表达数据位,其结果就是溢出错误,只有用N+1 bit才能表示相加后的结果,所以你首先必须要让2个加数据先具备N+1 bit数据位,再相加,这样相加后的结果才能被正确表示,那么就需要先将2个加数进行位数扩展,那么扩展什么呢? 如果是无符号数,我们通常想到的就是在高位扩展0,这样就不改变其原值,只是增加了其数据位容量,对于有符号数,我们就是扩展其自己的符号位(正数扩展0,负数补码扩展1),这样也是不会改变原值。。。。。然后再相加,得到的结果截取低N+1位数据位+1位符号位,就得到了最后的正确结果。
作者: renminggong    时间: 2018-3-15 14:47
Booth乘法器可以解决你的问题
作者: 江山无限辉    时间: 2018-3-17 08:47
学些了,谢谢
作者: 小鸟探戈    时间: 2018-4-12 01:16
初学者很容易把补码的意义理解错误,我来试着给你解释一下。
看上面很多人讨论的时候,其实把好几个概念混在一起了。其实我觉得应该分开来讲。
1.补码的意义
其实我理解的补码的意义很简单,就是认为地创造一种二进制编码,使得两个有符号的补码相加或相乘的时候,在位宽能够装得下你的计算数的前提下,可以直接忽略溢出位。
如何能做到这样呢?其实很简单。我们拿加法来举例吧,这样比较简单。
我们假设有一个4比特的二进制数,如果我们要用他来表示有符号数。
比如6应该是0110,那如果我要如何在能满足我前面提到的要求的前提下,来表示-6?
即X+6=0+16。16是2的四次方,既然现在只有四比特,那两个数相加肯定只能再往上溢出1位。如果我们把这个溢出位忽略掉,那就相当于减去了16。所以我们用1010(十)来表示4比特有符号数-6。你可以用补码取反加一的方法去验证每一个数。如果是五比特有符号数,那溢出位忽略掉的就是32了,依此类推。
2.为什么要扩位?
其实扩位跟补码的运算法则没有半毛钱关系的。我上面说了,补码之所以要发明,就是为了两个数在相加时可以直接忽略溢出位。可以直接忽略,那为什么我们平时要扩位呢?
我前面说到了,加法运算的时候,我们需要考虑到数相加之后的你给的位宽能装得下你的结果。比如,一个4比特的有符号数,能表示的范围是-8~7。那如果两个4比特的有符号数相加,范围就是-16~14。这样的范围的数四比特是装不下的,必须要五比特才能装得下。所以为了防止这种我们不期望的溢出发生,就需要在相加之前先扩成五比特。但是如果你这两个数虽然是四比特,但其实你清楚他并不可能把范围用完,比如你只用到了-2~2,那你也可以不用扩位。
总之,作为一个数字电路设计工程师,心里必须要随时有一个很清晰的账本,每一个数的取值范围是多少。你在定义每一个数,在做每一次运算的时候都要确保你能装的下他。
3.如何扩位
当一个数你已经发现了在加法或乘法之后,当前位宽装不下了,那你要如何扩位呢。如果是无符号数或者有符号的正数,那很简单,直接高位补0就行了。
如果是负数,其实也很简单,就是理解上稍微麻烦一点。你可以仔细看我第一条负数的补码的推导过程。如果有一个数-N,四比特表示的方法就是16(2的四次方)-N。你现在要扩成五比特,那就需要表示成32(2的五次方)-N。没错,扩展的过程中,其实就是要加上2的四次方即可。这就是你一直被告知的原则,负数扩位高位补1。
我上面说的原理同样适用于乘法,有兴趣的话你可以自己去推,其实思路都是一样的。了解了这些,相信楼主不会再在理解补码时这么混乱了。
作者: 小鸟探戈    时间: 2018-4-12 01:26
补充一点,之所以要按照我上面说的原则发明补码,是因为数字电路其实是很蠢的。他并不知道你的数是有符号数还是无符号数,我们需要让电路在进行加法或乘法运算时,不论你给的是有符号数、无符号数、正数或是负数,他的映射规则都一模一样。
作者: yxccc1108    时间: 2018-5-11 14:43
回复 7# dlb05061131

偶然看到你这个文件,解决了设计中困惑很久的一个问题,非常感谢!
作者: jackstrive    时间: 2018-5-22 10:12
Thanks
作者: zhanghui110    时间: 2019-5-26 23:19
带符号的乘法运算,符号位异或计算,然后转化为原码用于乘法计算,最后得出的结果再转换为补码
作者: leolew    时间: 2021-6-16 15:01


   
glace12123 发表于 2018-1-5 21:37
你对有基2补码的理解是错误的。。。。2个N位的有符号数相加,必须各自扩展符号到MSB上形成2个N+1位数相加, ...


感谢,解释的好清楚,解答我的困惑了!
作者: leolew    时间: 2021-6-16 15:03
多谢楼主和各位的分享,解决了困惑我好久的问题,受益匪浅!
作者: smarthb    时间: 2021-6-18 11:17
学习中…………




欢迎光临 EETOP 创芯网论坛 (原名:电子顶级开发网) (https://bbs.eetop.cn/) Powered by Discuz! X3.5