1、引言
计算机使用二进制存储数字。
简单来讲,由0和1两个数字组成的序列,称为二进制数。
在计算机科学中,二进制数主要有四种形式:原码、反码、补码、移码。
二进制数是由0和1两个数字组成的序列,这个序列中的每一个数字都有它自己的权重。从右
往左,这个序列每一位上的数字对应的权重依次是、
、
、
、
例如,二进制数0101表示正整数5
计算过程(二进制转化为十进制):
二进制数不仅能表示整数,而且能表示小数。
以小数点为分界点,小数点左侧,从右往左,每一位上的数字对应的权重依次是、
、
、
、
;小数点右侧,从左往右,每一位上的数字对应的权重依次是
、
、
、
例如,二进制数0.1110表示小数0.875
计算过程(二进制转化成十进制):
2、原码
虽然二进制数能表示整数和小数,但是只能表示正整数和大于零的小数。
为了能表示负整数和小于零的小数,引入原码。
规定二进制数中权重最大的位是最高位。
规定最高位是符号位的二进制数,称为原码。
符号位的数字是0,这样的二进制数表示正数。
符号位的数字是1,这样的二进制数表示负数。
原码的二进制布局示意图,如下图所示。
原码 = 二进制数
原码 =
二进制数
例如,原码0101表示正整数5
注释:符号位是0,数值位是101。原码 0101=
再比如,原码1101表示负整数-5。
注释:符号位是1,数值位是101。原码 1101=
再比如,原码101.1表示负数
注释:符号位是1,数值位是01.1。原码101.1=
原码的优点
能表示正数和负数。
原码的缺点
不能直接参与算术运算。
例如,原码0101表示正整数5,原码1001表示负整数-1,原码0101与原码1001按位相加
得到原码1110,原码1110表示负整数-6。
按位相加的规则:0+0=0,0+1=1,1+0=1,1+1=0向前(右)进一位。
计算过程如下图所示。
显然,5+(-1)= 4 ≠ -6,两个原码按位相加的结果是错误的。
因此,原码不能直接参与算术运算。
此外,原码存在正零和负零。
符号位是0,其余各位都是零的原码,表示正零。
符号位是1,其余各位都是零的原码,表示负零。
例如,原码0000表示正零,原码1000表示负零。
3、反码
虽然原码能表示正数和负数,但是不能直接参与算术运算。为了解决这个问题,引入补码。
补码能直接参与算术运算。
在介绍补码之前,先给出反码的定义。
反码视为原码和补码之间的过渡形式。
反码与补码的关系:反码的最低位加1,得到补码。
反码与原码的关系:反码的最高位和原码的最高位相同,正数的反码和原码完全相同。
负数的反码,由负数的原码按照一定规则转化得到。负数的原码最高位(符号位)保持不变,
其余各位取反,按照这样的规则得到负数的反码。
例如,反码0101表示正整数5
注释:符号位是0,说明这个反码表示正数。正数的反码和原码完全相同。因此,反码0101
对应原码0101,原码0101表示正整数5。
再比如,反码1101表示负整数。
注释:符号位是1,说明这个反码表示负数。负数的反码和原码的符号位相同,其余各位取反,
得到原码1010。原码1010代表负整数。
再比如,反码101.0表示负数 。
注释:符号位是1,说明这个反码表示负数。负数的反码和原码的符号位相同,其余各位取反,
得到原码110.1。原码110.1表示负数。
反码的优点
不仅能表示正数和负数,而且能直接参与算术运算。
例如,反码0101代表正整数5,反码1001代表负整数-6。反码0101与反码1001按位相加,
得到反码1110,反码1110代表负整数-1。
注释:反码0101的符号位是0,反码0101对应原码0101,原码0101表示正整数5。
注释:反码1001的符号位是1,反码1001对应原码1110,原码1110表示负整数-6。
注释:反码1110的符号位是1,反码1110对应原码1001,原码1001表示负整数-1。
按位相加的规则:0+0=0,0+1=1,1+0=1,1+1=0向前(右)进一位。
计算过程如下图所示。
显然,5+(-6)= -1 ,两个反码按位相加的结果是正确的。
因此,反码能直接参与算术运算。
反码的缺点
虽然反码能直接参与算术运算,但是存在一个弊端。两个反码相加的过程中,如果符号位
出现进位(1+1=0),那么最低位需要加1。
例如,反码1110与反码0011按位相加,得到反码0001。由于这两个反码相加的过程中,
符号位出现进位,最低位需要加1。最终的计算结果是反码0010。反码0010表示正整数2。
显然,(-1)+(3)= 2,两个反码按位相加的结果是正确的。
注释:反码1110表示负整数-1,反码0011表示正整数3,反码0010表示正整数2。
计算过程如下图所示。
再比如,反码1110.0与反码0011按位相加,得到反码0001.0。由于这两个反码相加的过程中,
符号位出现进位,最低位需要加1。最终的计算结果是反码0001.1。反码0001.1表示正数1.5。
显然,(-1.5)+(3)= 1.5,两个反码按位相加的结果是正确的。
注释:反码1110.0表示负数-1.5,反码0011表示正整数3,反码0001.1表示正数1.5。
计算过程如下图所示。
此外,反码存在正零和负零。
符号位是0,其余各位都是0的反码,表示正零。
符号位是1,其余各位都是1的反码,表示负零。
例如,反码0000表示正零,反码1111表示负零。
注释:反码0000的符号位是0,反码0000对应原码0000,原码0000表示正零。
注释:反码1111的符号位是1,反码1111对应原码1000,原码1000表示负零。
4、补码
虽然反码能直接参与算术运算,但是存在一些弊端。两个反码相加的过程中,如果符号位
出现进位(1+1=0),那么最低位需要加1。此外,反码存在正零和负零。为了解决这些问题,
引入补码。
补码能直接参与算术运算。而且,两个补码相加的过程中,当符号位出现进位(1+1=0),
最低位不需要加1。这大大提高了运算效率,降低了算术运算过程的复杂度。此外,补码
不存在正零和负零之分。使用补码表示零,只有一种表示形式:每一位上的数字都是零。
补码与反码的关系:反码的最低位加1,得到补码。
补码与原码的关系:补码的最高位和原码的最高位相同,正数的补码和原码完全相同。
负数的补码,由负数的原码按照一定规则转化得到。负数的原码最高位(符号位)保持不变,
其余各位取反,然后最低位加1,按照这样的规则得到负数的补码。
例如,补码0101表示正整数5
注释:符号位是0,说明这个补码表示正数。正数的补码和原码完全相同。因此,补码0101
对应原码0101,原码0101表示正整数5。
再比如,补码1001表示负整数-7
注释:符号位是1,说明这个补码是负数。负数的补码和原码的符号位相同,其余各位取反,
然后最低位加1,得到原码1111。原码1111表示负整数-7。
再比如,补码1001.1表示负数-6.5
注释:符号位是1,说明这个补码是负数。负数的补码和原码的符号位相同,其余各位取反,
然后最低位加1,得到原码1110.1。原码1110.1表示负数-6.5。
补码的优点
能表示正数和负数。
能直接参与算术运算。与反码相比,算术运算过程的复杂度更低。
例如,补码0101代表正整数5,补码1011代表负整数-5。补码0101与补码1011按位相加,
得到补码0000。这两个补码相加的过程中,符号位出现进位。与反码不同,在符号位出现
进位的情况下,补码的最低位不需要加1。最终的计算结果就是补码0000。补码0000表示零。
显然,(5)+(-5)= 0,两个补码按位相加的结果是正确的。
注释:补码0101的符号位是0,补码0101对应原码0101,原码0101表示正整数5。
注释:补码1011的符号位是1,补码1011对应原码1101,原码1101表示负整数-5。
注释:假设机器字长是4bit,补码0101和补码1011按位相加,得到补码0000。这两个
补码相加的过程中,符号位出现进位(1+1=0)。受到机器字长的限制,二进制数的
位数不超过4位,补码0000的符号位左侧的位是不存在的。因此,最终得到的补码就是0000。
计算过程如下图所示。
再比如,补码0101.1代表正数5.5,补码1011代表负整数-5。补码0101.1与补码1011按位相加,
得到补码0000.1。这两个补码相加的过程中,符号位出现进位(1+1=0)。补码0000.1的符号位
左侧的位是不存在的。因此,最终得到的补码是0000.1。补码0000.1表示正数0.5。
显然,5.5+(-5)=0.5,两个补码按位相加的结果是正确的。
注释:补码0101.1至少占用5bit。不妨设机器字长是5bit,并且二进制数最低位的权重是。
注释:补码0101.1的符号位是0,补码0101.1对应原码0101.1,原码0101.1表示正数5.5。
注释:事先假设机器字长是5bit,并且二进制数最低位的权重是。因此,补码1011事实上
是补码1011.0。补码1011.0的符号位是1,补码1011.0表示负数。补码1011.0对应原码1101.0,
原码1101.0表示负整数-5。
注释:补码0000.1的符号位是0,补码0000.1对应原码0000.1,原码0000.1表示正整数0.5。
计算过程如下图所示。
此外,使用补码表示零,只有一种表示形式:每一位上的数字都是零。
补码不会出现正零和负零之分。
与原码及反码相比,补码在算术运算、零的表示这两方面更具优势。因此,计算机普遍使用
补码存储数据,尤其是整数类型数据。然而,对于小数类型数据,受到机器字长的限制,
如果计算机使用补码存储小数类型的数据,那么实际上存储的可能是小数的近似值。
举例来讲,小数0.1对应的二进制数是一个无限循环的由0和1组成的序列0.0001100110011
0011••• 。计算机使用二进制存储数据,假设使用补码的形式去表示这个二进制数,那么这个
二进制数对应的补码的位数必然无限的。由于受到机器字长的限制,计算机实际只能保留有限
位数的补码。这意味着计算机实际存储的是0.1的近似值。
当然,某些小数能被精确表示。例如,小数0.5对应的二进制数是0.1。不妨设机器字长是4bit,
并且最低位的权重是2^(-2)。小数0.1对应的二进制数事实上是00.10。二进制数00.10对应原码
00.10,原码00.10对应补码00.10。因此,某些小数能被精确表示。
事实上,计算机不使用补码去存储小数类型数据,而是使用“移码+原码”的组合形式去存储小数
类型数据。这里不展开介绍,关于小数类型数据的存储方式,敬请关注后续推文。
总之,计算机使用补码存储整数类型数据,使用补码对整数类型数据进行算术运算。计算机使用
“移码+原码”的组合形式去存储小数类型数据,使用这种组合形式对小数类型数据进行算术运算。
注意事项
特别规定:符号位是1,其余各位都是0的补码,表示负整数 。其中,n代表补码的数值位
的位数。例如,补码1000表示负整数-8。补码1000 0000表示负整数-128。
这种特殊的补码的二进制布局示意图,如下图所示。
这种特殊的补码没有对应的原码,或者说这样的补码不能转化为原码。
补码的缺点
任意两个补码不能直接比较大小。
具体来说,任意两个补码比较大小,必须先判断两者的符号位。
如果两者的符号位不同,那么符号位是0的补码一定大于符号位是1的补码。
如果两者的符号位相同,那么也不能直接判断两者的大小。需要根据符号位的具体情况,
分类讨论。
例如,补码0001和补码0111的符号位都是0,补码0001小于补码0111。
注释:补码0001表示正整数1,补码0111表示正整数7。
再比如,补码1111和补码1001的符号位都是1,补码1111大于补码1001。
注释:补码1111表示负整数-1,补码1001表示负整数-7。
5、移码
为了能直接判断两个二进制数的大小,引入移码。
移码是一种特殊的二进制数。
移码不能直接参与算术运算。但是,与补码相比,移码具有一个突出优势,那就是任意两个
移码能直接比较大小。具体来说,移码0000 ~ 1111构成一个严格单调增大的数列{ 0000,0001,
0010,0011,0100,0101,0110,0111,1000,1001,1010,1011,1100,1101,1110,
1111}。
换而言之,移码0000 < 移码0001 < 移码0010 < ··· < 移码1110 < 移码1111
移码与补码的关系:移码,由补码按照一定规则转化得到。补码的符号位取反,其余各位保持
不变,按照这样的规则得到移码。这样的移码,称为“标准的移码”,偏移值是,其中k是
移码的位数。
移码有符号位。但是,移码的符号位是1表示正数,移码的符号位是0表示负数。
例如,移码0101表示负整数-3
注释:移码0101对应补码1101,补码1101对应原码1011,原码1011表示负整数-3。
再比如,移码1111表示正整数7
注释:移码1111对应补码0111,补码0111对应原码0111,原码0111表示正整数7。
再比如,移码0000表示负整数-128
注释:移码0000对应补码1000,补码1000表示负整数-128。
移码的优点
任意两个移码能直接比较大小。
移码的用途
在计算机科学中,利用移码的独特优势,使用移码表示浮点数的指数域。这样的做法,有助于快速
比较两个浮点数的指数大小。
移码的缺点
不能直接参与算术运算。
此外,值得一提的是,在某些特定的情况下,会使用非标准的移码。标准的移码与非标准的移码的
区别在于偏移值。
偏移值等于(k是移码的位数)的移码,称为标准的移码。
偏移值不等于(k是移码的位数)的移码,称为非标准的移码。
IEEE 754技术标准选用的移码是非标准的,它的偏移值是。其中,k是移码的位数。
无论是标准的移码,还是非标准的移码,都能直接比较大小。
偏移值不会对移码的大小判断造成影响。
标准的移码转化为十进制数的计算公式:把标准的移码视为“不带符号位的二进制数”,把“不带
符号位的二进制数”转化为十进制数,再减去偏移值(k是移码的位数),最后的计算结果
就是标准的移码所对应的十进制数。
非标准的移码转化为十进制数的计算公式:把非标准的移码视为“不带符号位的二进制数”,把
“不带符号位的二进制数”转化为十进制数,再减去偏移值(可能是,根据实际情况
确定),得到的计算结果就是非标准的移码所对应的十进制数。