原码、反码、补码的分析,为啥正数的反码和原码相同,负数的反码等于原码的非符号位取反

之前学习的原码、反码、补码之间的转换,都是课本上教的,但是不知道为啥要那么转换,最近在看《编码》这本书,讲到加法器的实现,又看到了补码,就决定把这几个的关系研究研究

以下故事纯属虚构,为了直观简单所有运算都是按照4bit运算来讲解,4bit最多表示16个数,0~15 或者-8~7 所以别越界哦,下面开始讲故事 ~

很早以前有个路人甲发明了二进制,逢二进一,3就用(0011=0*2³ + 0*2² + 1*2¹ + 1*2º)表示,2就用(0010=0*2³ + 0*2² + 1*2¹ + 0*2º)来表示,那么负3和负2用啥来表示呢,突然想到手写的10进制中,负数是在前面加一条横杠比如-6、-2,所以想二进制也用加一条"横杠"来表示负数吧,所以在最左边是1(横杠)的就是负数,不是1的就是正数。

后来发明了计算机,由于计算机实现加法运算还算容易,实现减法非常复杂(会在另一篇文章讲解加法器),所以就只有加法器,那么减法运算只能转化成加法,减一个数等于加一个数的负数,然后计算机就用路人甲发明的表示负数的方法进行运算

(1) 2+3 = 0010 + 0011 = 0101 = 5      俩正数相加还是不错的 ~

(2) 3-2 = 3 + (-2) = 0011 + 1010 = 1101 = -5    what?咋是-5 ,仔细看发现,只要是一个正数一个负数相加,最左边肯定是1,那就肯定是负数了 ,不合理

(3) 2-3 = 2 + (-3) = 0010 + 1011 = 1101 = -5  what? 同上 ~

(4) -2 -3 = -2 + (-3) = 1010 +1011 = 0101 = 10101 因为符号位相加导致溢出,最高位溢出结果为0101 = 5 

(5) 0 + 0 = 0000 + 0000 = 0,   0 - 0 = 0  + (-0) = 0000 + 1000 = 1000 = -0, -0 - 0 = -0 + (-0) = 1000 + 1000 = 0000 = +0    运算没错,只是有正负0而已

从上面可以看出,符号位参与运算是有很大问题的,导致运算结果不对,那么怎么才能不让符号位进行运算呢 ~ 很简单,路人乙想到了一个号方法

反码要产生了

路人乙想 :符号位的产生,就是为了表示负数的,如果我表示负数的时候不用符号位,用另一种方式,是不是可以呢,结果就想到了用相反数表示负数,比如3 = 0011,因为二进制非0即1,所以-3可以将3(0011)按位取反得到1100,用1100表示-3,啊呀,这样不就没有符号位了。“反码”就产生了,用相反数表示负数的编码方式叫反码,那路人甲原来发明的编码方式就叫负数的"值"等于正数的"值"所以叫原码,(但是这种表示方式在读取负数的值的时候是不方便的,比如给你个1100,你必须得转换成他对应的正数0011(3),才知道是0011的反码,是"负0011"(-3))

那原码和反码啥关系呢 原码表示3用0011 反码表示3也用0011 ,所以正数的原码等于反码,原码表示-3用1011,反码表示-3是1100  所以负数的反码等于原码非符号位取反


原码、反码、补码的分析,为啥正数的反码和原码相同,负数的反码等于原码的非符号位取反_第1张图片

这是百度百科对反码的解释,不要把反码和相反数弄混了,反码专门就是给咱们程序员造的词 ~

接下来咱们看看没有符号位的反码进行运算,结果会怎么样呢, 注意结果也是反码,下面已经没有符号位这一说了

(1) 2+3 = 0010 + 0011 = 0101 = 5      正数和原码一样,所以结果也是对的

(2) 3-2 =  0011 - 0010 = 3 + (-2) =  0011 +1101 = 10000 最高位溢出结果为0000 = 0,应该是1结果是0 

(3) 2-3 = 0010 - 0011 = 2 + (-3) = 0010 + 1100 = 1110(反码,是一个负数) = -0001(转成正数) = -1 可以

(4) -2 -3 =  -2 + (-3) = 1101 +1100  = 11001 最高位溢出后是1001 = -6 应该是-6结果少了1变成-6了

(5) 0 + 0 = 0000 + 0000 = 0000 = +0

      0 - 0 = 0  + (-0) = 0000 + 1111 = 1111 = -0,

     -0 - 0 = -0 + (-0) = 1111 + 1111 = 1 1110 = 1110 这个不对了,不管是+0还是-0都不是

仔细观察可以发现以下规律

(1) 正数 + 正数 = 正数 ----没问题

(2) 正数 + 负数 = 正数------结果少1

(3) 负数 + 正数 = 负数-----没问题

(4) 负数 + 负数 = 负数------结果少1

(5) -0 + -0 是不对的 -----结果多1

补码要产生了

眼看路人乙的想法也行不通,路人丙看到上面的规律(1)发现全是正数加是没问题的,从(2)可以看出,如果负数的表现形式在之前反码的表现形式上再补个1,那么(2)就没问题了,结果发现(4)也变得没问题了。因为是是补了个1得来的,那就叫补码吧 ~ 下面咱们来运算一下看看

公式由之前的 (负数 = 正数各位取反)  ---->  (负数 = 正数各位取反 + 1)

(1) 2+3 = 0010 + 0011 = 0101 = 5      正数 + 正数是和反码没区别的,所以没问题

(2) 3-2 =  0011 - 0010 = 3 + (-2) =  0011 +(1101 + 1) = 0011 +1110 = 10001 最高位溢出结果为0001 = 1

(3) 2-3 = 0010 - 0011 = 2 + (-3) = 0010 + (1100 +1)  =  0010 + 1101 = 1111  补码运算,得到的也是补码,那这个补码是个负数,对应的值是多少呢,根据公式 (负数 = 正数各位取反 + 1) 则1111-1 = 1110 再取反 是0001  = 1,所以1111是正1的补数,那就是负一

(4)-2 -3 =  -2 + (-3) =(1101 + 1) + (1100 + 1) = 11011 = 最高位溢出后是1011 对应的正数是                                    5((0101)),所以是5的补码,那就是负5


哇,运算的问题终于解决了,但是补码只是用来运算的,他的负数表示太不直观,所以最终定的是,二进制的正负数还是用原码表示(直观,咱们开发者变成的时候还是用原码便是),但是运算的时候用补码运算(数值在计算中就是用反码存的,运算也是用反码)


补充

上面补码的产生说的是根据反码,找出来的规律 ~这是我自己编的。。。。。,但是补码运算确实是没问题的

下面说说正经的,为啥叫补码呢,可能就是有点互补的意思的,A+B = 固定的数。A和B是互补的。从搜索的资料来看,大部分说的是根据“模”时钟启发来的。比如时钟一共12个数,1到12点,那么2+10 = 12、4+8=12,那2和10,4和8都是互补的。比如现在指针指向12点,想让时钟指到 1点,可以顺时针+1 也可以逆时针 减11 ,也就是 -(12-1)。想让时钟指向10点,可以逆时针 -2 也可以顺势针 +10。结论就是

顺势针拨的数(也就是绝对值) + 逆时针拨的数(也就是绝对值) = 总的个数,两个数还是对于12说是互补

听起来有那么点道理,但是现在是中午十二点,我顺势针+1是下午1点,逆时针-11是凌晨1点,位置是一样,可他妈时间不一样啊 ~ 带着疑问运算一把看看,咱们就只运算那两个之前有问题的(2)和(3),首先咱们举例都是4bit进行运算的也就是 内存占用是这样的 

口口口口 

四个格子,能表示的数是2的4次方 = 16也就是0-15,再注意一点就是用这种方式代替负数就是不想有符号位,所以下面的运算中所有的数最高位不代表符号,是数值位

(2) 3-2 = 3 + (16 -2) = 3 + 14 = 0011 + 1110 = 10001 去掉溢出的最高位是0001 = 1

(4)-2 -3 = (16 - 2) + (16 - 3) = 14 + 13 = 1110 + 1101 = 11011 = 最高位溢出 1011 是11

啊呀,一看用"模"运算也不对啊 ~到底咋回事 ~ 突然想到我的疑问,下午一点和凌晨一点能一样吗? 答案是 不一样 !

接下来继续讲解怎么就对了

把上面的(2)(4)再说一下

(2) 3-2 = 3 + (16 -2) = 3 + 14 = 0011 + 1110 = 10001 去掉溢出的最高位是0001 = 1,咱们本来是-2结果变成了+14,虽然位置一样,但是多转了一圈啊。咱们再看右边 10001,溢出以后咱们把最高位丢弃了,只剩0001,那么丢弃的最高位代表的就是16,就是一圈啊,等号左边多加了一圈,右边减了一圈,所以没毛病 ~按照上面的公式  3-2 = 0011 + 1110 = 10001,那-2可以用1110代替,那-2就是2(0010)取反加一

(4)-2 -3 = (16 - 2) + (16 - 3) = 14 + 13 = 1110 + 1101 = 11011 = 最高位溢出 1011 是11(在这没符号位这一说)

那这个怎么解释呢?

左边多了两圈,右边溢出一个,少了一圈,那右边还有多的一圈,说明1011还是多了一圈的,所以虽然结果是1011(11),但是他是多了16的,所以实际应该是-5,1011代表的-5也就是正5取反 + 1

(5) -0 - 0 = -0 + (-0) = (16 -0) + (16 -0) = (1111 + 0001)  + (1111 + 0001) =  1111 + 1111 + 0001 + 0001 =  1110 + 0010 =   1 0000 去掉最高位后是0000 是0,但是根据上面说的,0000应该还得减去一圈那就是0000代表的是-16

仅仅是表示而已,比如一个字节是8位,如果有符号的话表示-128 ~ 127 但是你用过-128 吗


总结一下:

(1)原码、反码、补码,是互相独立的,不存在依赖关系,只是之间的表示方法存在规律

(2)原码、反码、补码都是针对负数怎么表示研究出来的。正数的表示方法都是统一的,负数的数值和正数的数值表示方法一样的方式就是原码、负数是正数各位取反的表示方式就叫反码、负数是正数各位取反再补上个1的表示方式就叫补码

(3)原码的形式可以很直观的表示出负数,所以在书面书写时,一般用原码,比如编程,但是因为计算问题,计算机存储的时候都是存储的补码,计算机只管存储,计算,不在意是否是正数,负数。正负是由编译器和操作系统自己的规则决定的。

你可能感兴趣的:(原码、反码、补码的分析,为啥正数的反码和原码相同,负数的反码等于原码的非符号位取反)