正数的原码=反码=补码。
计算机考虑芯片设计成本只做了加法器,并没有做减法器,引进补码的作用是为了让计算机在没有减法器的情况下做减法。
二进制使用“补码”方式表示负数,最高位叫“符号位”,符号位为1表示负数,符号位为0表示正数。
对于负数来说:
原码,所有位不变,最高位为1,表示负数。
反码,符号位不变,其余个位取反。
补码,符号位不变,其余个位取反,最后位+1(反码最后位+1)
原码
将一个整数,转换成二进制,就是其原码;
反码
负数的反是在其原码基础上,符号位不变,其余个位取反。
符号位加上真值的绝对值,即用第一位表示符号,其余位表示值。
如单字节5的原码为:000 0101;-5 的反码为:1111 1010;
补码
为了便于CPU进行减法运算。
负数的补码是在其原码的基础上,符号位不变,其余各位取反,最后+1。(即在反码基础上加1=补码)。
如单字节:5的原码为:0000 0101;-5的补码为:1111 1011;(二进制逢二进一)
负数用补码表示,如单字节 -5,在计算机中表示为:1111 1011
计算机为什么要使用补码呢?
首先,根据运算法则减去一个正数等于加一个负数,即:1-1=1+(-1),所以计算机被设计成只有加法而没有减法,而让计算机辨别“符号位”会让计算机的基础电路设计变得十分复杂,于是就让符号位也参与运算,从而产生了反码。
用反码计算,出现了“0”这个特殊的数值,0带符号是没有任何意义的。而且会有【0000 0000】和【1000 0000】两个编码表示0.于是设计了补码,负数的补码就是反码+1,正数的补码就是正数本身,从而解决了0的符号以及两个编码的问题,用【0000 0000】表示0,用【1000 0000】表示-128。
注意-128实际上是使用以前的-0的补码来表示,所以-128并没有原码和反码。使用补码,不仅仅修复了0的符号以及两个编码的问题,而且还能够多表示一个最低数。这就是为什么8位二进制,使用补码表示的范围为【-128,127】。
计算机如何做减法?
电脑中没有减法器,加法器需要通过集成电路来实现,而再搭一套减法器需要又一套集成电路。
计算机科学家就开发出了一套方法,使用加法器就可以算出减法。必须把减法转换成加法来运算。
补码运算
减掉一个数相加于加上一个负数,直接加负数不方便,可以加上这个负数的补码
使用进制计算 5 - 3,5与3分别对应的二进制如下
十进制 二进制
1 0001
2 0010
3 0011
4 0100
5 0101
使用人的逻辑算5-3很简单,结果就是2,但问题是计算机没有减法器,无法计算这套逻辑,必须把这套计算转换成加法,就引入了补码的概念。
101 = 5
011 = 3
010 = 2
负数的补码就是把它绝对值的最高位保持不变,但是其它位取反加1。
5-3
101 = 5
-011 = 3
-3 的绝对值是3
001
5-3=2
-3的补码就是7
减一个数,等于加上这个数的负数,直接加负数无法加,可以加上这个负数的补码。
人计算上面的逻辑觉得非常复杂,一团乱麻,但是对于计算机来说是非常有优势的,这个优势对机器来说非常容易实现,整个减法运算用到了加法用到了取反运算,取反运算用计算机非常容易实现,通过非门电路实现成本非常低,不需要单独再做减法器,使用加法器再加上一些非门电路就可以实现减法。
负数在计算机中是以补码的方式存储的,在探求为何计算机要使用补码前,需要先了解原码,反码和补码的概念。
对于一个数,计算机要使用一定的编码方式进行存储。原码、反码、补码是计算机存储一个具体数字的编码方式。
一个数在计算机中的二进制表示形式,叫做这个数的机器数。机器数是带符号的,在计算机中用一个数的最高位存放符号,正数为0,负数为1。
十进制中的数+2,计算机字长为8位,转换成二进制就是【00000010】
十进制中的数-2,计算机字长为8位,转换成二进制就是【10000010】
因为第一位是符号位,所以机器数的形式值就不等于真正的数值。例如上面有符号数【10000010】,其最高位1代表负,其真正数值是-2,而不是形式值130(【10000010】转换成十进制等于130)。所以将带符号位的机器数对应的真正数值称为机器数的真值。
从软件上考虑,原因有两个
1、表示范围
拿单字节来说,无符号型,其表示范围是【0,255】,总共表示256个数据。有符号型,其表示范围是【-128,127】;
无符号:0表示为 0000 0000,255表示为1111 1111,刚好满足了要求,可以表示256个数据;
有符号:若是用原码表示,0表示为0000 000。因为有符号,所以应该有个负0(虽然它还是0):1000 0000;
那么这样一来还能够表示256个数据么?
正数,没问题,127是0111 1111,1是0000 0001,当然其它的没有问题。
负数,-1是1000 0001,那么把负号去掉,最大的数是111 1111,也就是127,所以负数中最小能表示的数据是-127。
这样似乎不太对,该如何表示-128?直接用原码无法表示,而我们却有两个0;
如果我们把其中的一个0指定为-128,不行么?这样一来会产生两个问题:一是它与-127跨度过大;二是在用硬件进行运算时不方便;
所以,在计算机中,负数采用补码表示。
如 单字节-1,原码为1000 0001,反码为:1111 1110,补码为1111 1111,计算机中的单字节-1就表示为1111 1111。
单字节-127,原码是1111 11111,反码1000 0000,补码是1000 0001,在计算机中单字节-127表示为1000 0001。
单字节-128,原码无法表示,除了符号位,最大的数只能是127,在计算机中表示为1000 0000。
2、大小的习惯
以单字节数据为例。有符号数中,正数的范围是【1,127】,最大的是127,不考虑符号位,其表示为111 1111;最小的是1,不考虑符号位,其表示为000 0001;
负数中,最大的是-1,我们就用111 1111表示其数值部分。后面的数据依次减1,减到000 0001的时候,我们用它标示了-127。再减1,就变成000 0000了。还好我们有符号位,所以有两个0.把其中带符号的0拿过来,表示-128,刚好可以满足表示范围。
总结
CPU设计了加法器,考虑集成电路成本,没有设计减法器,又想计算减法,所以引入了补码的概念,将减法转换成加法。
使用补码计算加法,无非就是使用非门电路,对于计算机来说逻辑非常简单。
正数:原码=反码=补码
负数:最高位为“符号位”表示-号,原码不变,反码:除“符号位”外其余位取反。补码:反码结果+1(逢二进一)。
然后就可以使用加法计算减法喽。