二进制原码、反码、补码
涉及到的1的个数、移位、计算等问题
今天在刷题的时候遇到一些二进制数的问题,计算机存储数据用补码存储,但是涉及到计算、移位等问题有点难理解,老是绕不过来,怕后面遇到问题无法融会贯通,所以来总结一下:
首先详细说明一下原码、反码、补码的机制:
至于计算机中以二进制码存储数据这个点应该是很清楚了,二进制码有原码、反码、补码三种形式
原码
是最简单的机器数表示法。用最高位表示符号位,‘1’表示负号,‘0’表示正号。其他位存放该数的二进制的绝对值。
从十进制直接转换为二进制数据 得到的形式就是原码了,比如(以8位数据表示):
```
十进制数 32
转换为二进制就是 0001 0000
```0001 0000这就是二进制原码了。
最高位为符号位,0表示正,1表示负。
下图给出部份正负数数的二进制原码表示法
接下来看看计算机用原码计算的情况:
1. 0000 0001+0000 0010=0000 0011 1+2=3;没有问题🙂
2. 0000 0000+1000 0000=1000 0000 0+(-0)=(-0); 好像问题也不大😐
3. 0000 0001+1000 0001=1000 00110 1+(-1)=-2; ???🙃很明显,不对
为什么呢:
当然是符号位引起的,所以原码,虽然直观易懂,易于正值转换。但用来实现加减法的话,运算规则总归是太复杂。所以需要反码
反码
反码的获取要分正负:
正数的反码是其本身,与原码相同;
负数的反码就是其原码除符号位外按位取反。
下图给出部分正负数的二进制数反码表示法
用反码再次计算 1+(-1):
0000 0001反+1111 1110反=1111 1111反=1000 0000原 (1+(-1)= - 0);so,问题解决。
但是!!!再来看看两个负数相加:
1111 1110反+1111 1101反=0000 0011反=0000 0011原 ??很明显不对,两个负数相加就成正数了,所以虽然解决了正负相加的问题,但那时两个负数计算还是存在问题。
所以大佬们又想办法了,也就来到了补码。
补码
补码的转换也是要分正负滴:
正数的补码是其本身,与原码相同;
负数的补码等于反码+1。
好,总结一下就是正数三码相同,负数三码均不同,负数已哭晕在厕所😭
再去算上面的就不会有任何问题了,就不在下面罗嗦了,可以自己在草稿本上一写。
在《计算机组成原理中》,补码的另外一种算法是:
负数的补**码等于他的原码自低位向高位,尾数的第一个‘1’及其右边的‘0’保持不变,左边的各位按位取反,符号位不变。**
记住一点:计算机以二进制的补码存储和计算数据。
好,上面这是网上遍地都能看到的基本解释,但是到底为什么要这样设置三种码?单纯为了好算加法?而且有没有发现计算的时候原码、反码、补码来回切得多麻烦?
之前可能直接计算结果,也不管他计算机怎么算的所以就没什么困难感,可是等到做“二进制1的个数计算时”(听起来很简单有没有,做起来也简单但是看到大神的题解时。。嗯自己去看)反正就是正要理解时无法对这三种码熟练的归为己用!!!
没完,下面看到一个博主是这么说的:
总的来说,遍地的解释只是告诉我们怎么样可以计算得到补码,也就是说我们理解的只是一道题的一种解答方法,只求除了答案却不知道考点在哪。
那我们就来聊一聊考点,知道了考点相同类型的题就好说了。
补码的思想
补码的思想类似于生活中的时钟。
先来看时钟。
假设现在是10点,我要把它调到8点有几种方法呢?简单,再往前走10个小时,或者直接后退2个小时。
也就是说:10-2=8;10+10=8;
10-2不用说了,10+10怎么个等于8法?时钟一圈12小时,往前走2个小时就到12点了,这时候再往前走就重新从1开始了,
所以(10+10=10+2+8=12+8=8),很好理解对吧
至于这个12在时钟运算中被称为模,。
再回顾一下上面,原码、反码、补码的引入是为了方便计算机做加减法,而把减法化为加法的思想就是加上一个相反数。但是引入的符号位无法计算正确。
而我们知道,一个类型数是有存储限制的,也即大小限制,对于有溢出的运算(模运算),减去一个数也就相当于加上这个数的同余数。
也就是说,我们不引入负数的概念,就可以把减法当成加法来算。所以接下来我们聊4位二进制数的运算,也不必急于引入符号位。因为补码的思想,把减法当成加法时并不是必须要引入符号位的。是不是还没有实感?来看例子吧
而且我们可以通过下面的例子,也许能回答另一个问题,为什么负数的符号位是‘1’,而不是正数的符号位是‘1’。
补码实例:
我们以四位二进制为例,先看四位二进制的模,即四位二进制的最大容量,也就是2^4=16=10000 B既然如此
0110(6)-0010(2)=0110(6)+1110(14)=10100(20=16+4)
但是减去2,从另外一个角度来说,也是加上(-2)。即加上(-2)和加上14其实得到的二进制结果除了进位位,结果是一样的。
而且在有符号位的四位二进制数中,能表示的只有‘-8~7’,而无符号位数(14)的作用和有符号数(-2)的作用效果其实是一样的。
那正数的补码呢?加上一个正数,加法器就直接可以实现。所以它的补码就还是它本身。
然后我们再来看看为什么负数的补码的求法为什么是反码+1
因为负数的反码加上这个负数的绝对值正好等于1111,再加1,就是1000,也就是四位二进数的模
而负数的补码是它的绝对值的同余数,可以通过模减去负数的绝对值,得到他的补码。
这是因为根据上面的补码图,做减法时,0001(1)+1111(-1)=0000
我们再也不需要一个1000来表示负0了,就把它规定为-8
负数与负数相加的问题也解决了1111(-1)+1110(-2)=1101(-3)
看上面那个图。
如果我们把-8当成负数的原点。那么-5的补码是多少呢?
-5=-8+3
-5的补码就是-8的补码加3
1000(-8) +0011(3)=1011(-5)
对于八位加法器的话,可以把-128当补码原点。十六位可以把-32768当补码原点。
是的,128是256(八位二进制数的模)的一半,32768是65536(十六位二进数的模)的一半
而且简单的是
补码原点总是最高位是‘1’,其他位是‘0’
所以做加法总是简单得可以口算。
二进制数的移位操作
移位运算符
<<:左移 高位左移后溢出,舍弃,低位补0。
例如,假设以一个字节(8位)存一个整数,若a为无符号整型变量,则a=64,即二进制数01000000时,左移一位时溢出的是0。而左移2位时,溢出的高位中包含1,则不符合上述结论。
- <<1左移一位相当于 64*2 =128没有超出255 ,所以此时 直接右边直接补0即可
- <<2左移2位就相当于64*2*2 = 256 超出了255的范围,所以此时超出255的就采取左边的二进制丢弃,右边补0
>>:右移 低位溢出的位数被舍弃,正数高位的空位用0补齐,负数高位的空位用1补齐。
例如:右移一位接下来讲一下其他位运算
2、 |:位逻辑或
3、^:位逻辑异或
4、~:位逻辑反
(1)逻辑与的运算规则:当进行两个数的逻辑与运算时,先将该两个数化为二进制,然后进行逻辑与运算,当相同位的数只有当双方都为1时,结果才为1,否则为0。