二进制原码、反码、补码

涉及到的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在时钟运算中被称为,。

也就是说, 10-2和10+10从另一个角度来看是等效的,它都使时针指向了八点钟。
既然是等效的,那在时钟运算中,减去一个数,其实就相当于加上另外一个数(这个数与减数相加正好等于12,也称为同余数

再回顾一下上面,原码、反码、补码的引入是为了方便计算机做加减法,而把减法化为加法的思想就是加上一个相反数。但是引入的符号位无法计算正确。
而我们知道,一个类型数是有存储限制的,也即大小限制,对于有溢出的运算(模运算),减去一个数也就相当于加上这个数的同余数。

也就是说,我们不引入负数的概念,就可以把减法当成加法来算。所以接下来我们聊4位二进制数的运算,也不必急于引入符号位。因为补码的思想,把减法当成加法时并不是必须要引入符号位的。是不是还没有实感?来看例子吧

而且我们可以通过下面的例子,也许能回答另一个问题,为什么负数的符号位是‘1’,而不是正数的符号位是‘1’。

补码实例:

我们以四位二进制为例,先看四位二进制的模,即四位二进制的最大容量,也就是2^4=16=10000 B
2的同余数,就等于10000-0010=1110(14)

既然如此

0110(6)-0010(2)=0110(6)+1110(14)=10100(20=16+4)

OK,我们看到按照这种算法得出的结果是10100,但是对于四位二进制数,最大只能存放4位(硬件决定了),如果我们低四位,正好是0100(4),正好是我们想要的结果,至于最高位的‘1’,计算机会把他放入psw寄存器进位位中。8位机则会放在cy中,x86会放在cf中(这个我们不作讨论)

但是减去2,从另外一个角度来说,也是加上(-2)。即加上(-2)和加上14其实得到的二进制结果除了进位位,结果是一样的。

如果我们把1110(14)的最高位看作符号位后就是(-2)的补码,这可能也是为什么负数的符号位是‘1’而不是‘0’。

而且在有符号位的四位二进制数中,能表示的只有‘-8~7’,而无符号位数(14)的作用和有符号数(-2)的作用效果其实是一样的。

那正数的补码呢?加上一个正数,加法器就直接可以实现。所以它的补码就还是它本身。


到这里,我们发现原码,反码的问题,补码基本解决了。在补码中也不存在负零了,因为1000表示-8

然后我们再来看看为什么负数的补码的求法为什么是反码+1

因为负数的反码加上这个负数的绝对值正好等于1111,再加1,就是1000,也就是四位二进数的模

而负数的补码是它的绝对值的同余数,可以通过模减去负数的绝对值,得到他的补码。

所以 负数的补码就是它的反码+1。
(反码+原码=1111=模-1)
(1111+1=模)
(原码+补码=模)
=》补码=反码+1

这是因为根据上面的补码图,做减法时,0001(1)+1111(-1)=0000
我们再也不需要一个1000来表示负0了,就把它规定为-8

负数与负数相加的问题也解决了1111(-1)+1110(-2)=1101(-3)

把原、补码联系上时钟:

看上面那个图。

如果我们把-8当成负数的原点。那么-5的补码是多少呢?

-5=-8+3

-5的补码就是-8的补码加3

1000(-8) +00113)=1011(-5)

对于八位加法器的话,可以把-128当补码原点。十六位可以把-32768当补码原点。

是的,128是256(八位二进制数的模)的一半,32768是65536(十六位二进数的模)的一半

而且简单的是

补码原点总是最高位是‘1’,其他位是‘0’

所以做加法总是简单得可以口算。

二进制三码机制了解之后看一些具体的问题

二进制数的移位操作

移位运算符

<<:左移    高位左移后溢出,舍弃,低位补0。

左移相当于乘以2的幂次。
例如:左移一位

左移一位相当于该数乘以2,左移2位相当于该数乘以2^2=4。上面举的例子15<< 2=60,即乘了4。但此结论只适用于该数左移时被溢出舍弃的高位中不包含1的情况

例如,假设以一个字节(8位)存一个整数,若a为无符号整型变量,则a=64,即二进制数01000000时,左移一位时溢出的是0。而左移2位时,溢出的高位中包含1,则不符合上述结论。

由下表可以看出,若a的值为64,在左移一位后相当于乘2,左移2位后,值等于256(100000000 是9位,255是:11111111 8位)。
  1. <<1左移一位相当于 64*2 =128没有超出255 ,所以此时 直接右边直接补0即可
  2. <<2左移2位就相当于64*2*2 = 256 超出了255的范围,所以此时超出255的就采取左边的二进制丢弃,右边补0

>>:右移  低位溢出的位数被舍弃,正数高位的空位用0补齐,负数高位的空位用1补齐。

例如:右移一位

接下来讲一下其他位运算

程序中的所有数在计算机内存中都是以二进制的形式储存的。位运算说穿了,就是直接对整数在内存中的二进制位进行操作。
在C语言中,提供了相应的进行位运算的操作符:
1、&:位逻辑与
2、 |:位逻辑或
3、^:位逻辑异或
4、~:位逻辑反
(1)逻辑与的运算规则:当进行两个数的逻辑运算时,先将该两个数化为二进制,然后进行逻辑与运算,当相同位的数只有当双方都为1时,结果才为1,否则为0。
eg:4&5=4  化为二进制:0100&0101=0100;
2逻辑或的运算规则:当进行两个数的逻辑运算时,先将该两个数化为二进制,然后进行逻辑或运算,当相同位的数只要有一方为1,结果就为1,否则为0。
eg:4|5=5  化为二进制:0100|0101=0101;
3)逻辑异或的运算规则:当进行两个数的逻辑异或运算时,先将该两个数化为二进制,然后进行逻辑异或运算,当相同位的数只有当双方都为不同数字时,结果才为1,否则为0。
eg:4^3=7  化为二进制:0100^0011=0111;
(4)逻辑反的运算规则:当进行一个数的逻辑运算时,先将这个数化为二进制,然后进行逻辑反运算,当数为1时,结果为0,当数为0时结果为1
eg:~12=-13 注:
按照上面的运算规则,我们先将12转换为二进制,为1100,进行位逻辑反运算,则为0011,化为十进制数结果应为3才对啊,那为什么测试的结果是-13呢?
原来啊,在计算机中,二进制的存储方式是以补码的形式存储的,12在计算机内存储的是0000 1100(第一个0位符号位,代表这个数是正数),对其进行逻辑反运算则为1111 0011,但这个二进制只是这个变量在计算机内的存储形式(即补码形式),以我们需要将其转换为原码形式,(用上面讲过的方法补码=模-补码=1000 0000-1111 0011=1000 11011000 1101转换为十进制数则为-13。

总结

最后,掌握了上述基本原理,对于二进制数的操作应该就不困难了。本文学习了一些博主的内容,链接贴在下方: