无符号数
计算机CPU中寄存器是用来存放数据的,通常称寄存器的位数为CPU的机器字长。所谓无符号数,就是没有符号的数(只能表示正数),在寄存器中的每一位均可用来存放数值,而与其相对的另一个概念——有符号数要复杂一些。因为有符号数还能表示负数,这就需要有一个数据位来表示数据的正负,所以无符号数是相对于有符号数而言的,指的是整个机器字长的全部二进制位均表示数值位,相当于数的绝对值。由于无符号数和有符号数的这一区别,在同一机器字长的条件下,无符号数与有符号数所能表示的数值范围也是不同的。例如,机器字长同为16位的情况下,无符号数的取值范围为0~65535,而有符号数如果采用补码表示法(关于补码下面会学到),取值范围则为:-32768~+32767.
有符号数
1.真值与机器数的概念
学习有符号数首先要理解两个专业术语:真值和机器数。
我们平时使用符号“-”来表示一个负数,例如-100,-32,使用“+”或省略“+”来表示一个正数,例如+65,130.这些例子中的实际值就是真值。真值往往是面向人的,可以用二进制数表示,也可以用其它数值表示,但根据习惯,常用十进制数表示。
而在计算机中,因为只能表示0和1两种数码,所以计算机中任何信息都是采用0和1的组合序列来表示。也就是说,对于有符号数来说,也只能用0或者1来表示“正”或“负”。一般情况下,都用0来表示“正”,用1来表示“负”,并且将符号位放在有效数字的前面,来组成一个有符号数。
一个数在机器中的表示形式称为机器数。形式上为二进制数,但有别于日常生活中使用的二进制数。例如,真值+107和-107对应的机器数:为0(符号位)1101011 1(符号位)1101011
机器数和日常生活中使用的二进制数(真值)比较,有如下一些特点:
(1)机器数为二进制形式,用0和1组合来表示数据,包括符号。机器数符号数码化,一般使用机器数的最高位为符号位,当符号位为0,表示为正数;符号位为1,表示为负数。
(2)小数点不直接出现。机器数通过一定的方式来表示数的小数点位置,有定点表示法和浮点表示法。
(3)机器数使用时需要明确采用的位数(字长),即一个机器数所占用的存储空间大小,以bit(一个二进制位)或Byte(一个字节,八个二进制位)为单位。相应的,不同机器数所能表示的真值是有一定取值范围的。
有符号数X采用三种编码方式记录一个真值——原码、反码和补码,分别记作:[X]原、[X]反和[X]补。为什么需要有符号数的不同编码方式呢?由于机器数对符号位进行了数码化。那么需要对计算机中的机器数进行四则运算时,符号位作为数据的一部分,也应当参与到运算中来。但是,如果参与运算,又如何对其进行处理呢?关于这些问题,在下面讲述的编码方式部分(补码和反码)中会提到。
2.原码表示法
原码表示法是一种最简单的机器数表示法,其最高位为符号位,符号位为“0”时表示该数为正,符号位为“1”时表示该数为负,数值部分与真值的绝对值相同。
若真值X为整数,整数原码的定义为:
[X]原=X 0<=X<2^n-1
[X]原=2^n-1+|X| 2^n-1<X<=0
其中,X为真值,n为整数机器字长。
从定义可以看出,正整数的原码就是其本身,负整数的原码取其绝对值,符号位置1即可(0表示正号,1表示负号)。即求一个数的原码:数值部分不变,符号“+”和“-”在最高位用0和1分别来表示即可。
【例】分别写出+1110和-1110两个二进制的原码。
当X=+1110时,[X]原=01110
当X=-1110时,[X]原=2^4-(-1110)=11110
若真值X是纯小数,则原码的定义为:
[X]原=X 0<=X<1
[X]原=1+|X| -1<X<=0
从定义可以看出,正的纯小数的原码就是其本身,而负的纯小数的原码可以通过将其绝对值的原码符号位置1得到。
【例】分别写出+0.1011和-0.1011两个二进制数的原码。
当X=+0.1011时,[X]原=0.1011
当X=-0.1011时,[X]原=1.1011
【例】写出X=0时的原码
当真值X=0时,符号位可以为“+”也可以为“-”;
当符号位为正时,[X]原=[+0.0000]原=0.0000
当符号位为负时,[X]原=[-0.0000]原=1+0.0000=1.0000
可见,若用原码表示机器数,0的表示不唯一,会出现+0和-0两种情况。
采用原码表示数时,表示范围为:-(2^n-1)~(2^n-1),其中n为机器字长。比如,8位原码表示数的范围为:-127~127.
原码表示简单易懂,而且与真值的转换方便。但原码表示的数不便于计算机运算,因为在两原码数运算时,首先要判断他们的符号,然后再决定用加法还是减法,致使机器的结构相应地复杂化或增加了机器的运算时间。补码表示法的作用就是用于简化数值运算时的复杂操作。
3.补码表示法
(1)补码的概念和作用。首先举例分析两个十进制的运算:
78-30=48
78+70=148
假设我们使用的是机器字长为2位的十进制数的运算器(现实中这样的运算器是不存在的),在做78+70时,由于机器字长只有2位,所以结果为148中的百位100超出该运算器的表示范围,最高位的100已经溢出,会被舍弃。也就是说在2位十进制数的运算器中做78+70得出的结果是48,与78-30得出的结果相等。
因此,在2位十进制数的运算器中做78-30的减法操作时,若用78+70的加法也能得到同样的结果。在数学上,这也称为同余式:
78-30≡78+(100-30)=78+70(mod 100)
上式中,100就是两位十进制数运算器的溢出量。在数学上称为模,用mod来表示。上述运算称为有模运算。事实上,在计算机中进行的运算本质上都是有模运算。我们引入补码的意图是想要简化数值运算的复杂操作,也就是找到一个与负数等价的整数来替代该负数,把减法操作转换为 加法操作,这样就可以在运算器中只用加法器,而无须再设置减法器。上例中我们可以看到,78-30的一个减法操作,在同余式中可以使用78+70这样一个加法操作代替,这样就可以简化运算器的结构了。
上述等式也可以这样写:
78+(-30)≡78+70(mod 100)
那么这个等式中的-30和70之间有什么关系呢?十进制数中,相对模100,-30的补码就是100-30=70,即该数与模相加,结果就是这个数的补码。也就是,在有模运算中,一个负数用其补码代替,将得到同样正确的运算结果。因此,在有模运算引入补码后,减法可以转换为加法。
在计算机中,数值运算受字长限制,都是有模运算。真值运算结果中的溢出部分,即模,在机器中是表示不出来的,若运算结果超出能表示的数值范围,则会自动舍去模,只保留小于模的部分。
【例】设A=68,B=42,使用补码运算方法求A-B(mod 80)。
A-B=68-42=26
对模80而言,-42可以用其补数80-42=38代替,即
-42≡80-42=+38(mod 80)
所以,使用补码代替原负数进行计算,得
A-B=68-42=68+(-42)≡68+38=26(mod 80)
进一步发现,在同余式中,对于一个负数,如-30,对模100而言
-30≡100-30≡70(mod 100)
而对于一个正数,如+30,
+30≡100+30≡30(mod 100)
可知,正数相对于“模”的补码就是正数本身,负数相对于“模”的补码为“模”+该负数。类似的,补码的概念可以应用到其它任意“模”上,尤其是二进制,计算机中的运算都是以二进制方式进行的。举例如下:
-4≡+6(mod 10)
+4≡+4(mod 10)
-3≡+97(mod 100)
+90≡+90(mod 100)
-1011≡+0101(mod 2^4)
+1011≡+1011(mod 2^4)
-0.1001≡+1.0111(mod 2)
+0.1001≡+0.1001(mod 2)
从上面的分析可以总结出如下一些结论:
A-B可以看作A+(-B)。而在有模运算中,负数-B可以用它的补码来代替,而它的补码可以用模+-(B)求得,这样减法操作在有模运算中就被改写成了加法操作。而一个正数的补码等于该正数本身。
模-B=A,反过来,A+B=模。即一个负数的绝对值和它的补码的绝对值的和等于模。
(2)补码的定义。二进制真值X的补码定义如下:
设机器字长为n位,若X是纯整数,则
[X]补=X 0<=X<=2^n-1 -1
[X]补=2^n+X -2^n-1 <=X<0
【例】设机器字长为8位,计算二进制整数10110和-10110的补码。
字长为八位的机器数的模是2^8=100000000
正数补码为其本身,所以[10110]补=00010110.
[-10110]补=100000000-10110=11101010。
再进一步,[-10110]原=10010110,与[-10110]补=11101010相比较,可以得出计算机负整数的补码的简便方法:
①首先获得负整数的原码;
②除符号位以外,将原码的其它位全部取反;
③然后将取反后的数据末位加一,可得负数的补码。
【例】设机器字长为8位,计算二进制数-1101的补码。
按照简便方法,首先,获取原码
[-1101]原=10001101
原码除符号位,全部按位取反,得11110010,再加1,得[-1101]补=11110011
【例】设机器字长为8位,计算二进制数0的补码。
当真值X=0时,符号位可以为“+”也可以为“-”:
当符号位为正时:[+0]补=00000000(mod 2^8)
当符号位为负时:[-0]补=100000000-00000000=00000000(mod 2^8)
由此可知,补码不会出现0的表示不唯一的情况,补码没有+0和-0之分,即与原码不同,补码中的“0”只有一种表示形式。事实上,使用补码表示一个字长n位的二进制整数,取值范围为:-2^n-1~2^n-1-1
设机器字长为n位,若X是纯小数,则
[X]补=X 0<=X<1
[X]补=2+X -1<=X<0 (mod 2)
【例】计算纯小数0.1011和-0.0110的补码。
根据公式定义,正数的补码就是其原码本身,得
[0.1011]补=0.1011
当小数为负数时,按照公式计算,得
[-0.0110]补=10.0000-0.0110=1.1010
仔细观察,发现负整数求补码的简便方法同时可以用于小数负数求补码,如上例中,
[-0.0110]原=1.0110,
除符号位全部取反,得1.1001,末位加1,得
[-0.0110]补=1.1001+0.0001=1.1010
反过来,根据补码公式定义,如果已知补码,还可以根据补码计算求得真值。
【例】若小数X的补码【X】补=1.0101,求X。
若[X]补=1.0101,则
X=[X]补-2=1.0101-10.0000=-0.1011
【例】若小数X的补码[X]补=0.0101,求X。
符号位为0,表示该小数表示的是一个正数,则
X=0.0101
【例】设机器字长为8位,若整数X的补码[X]补=11110011,求X。
符号位为1,表示该整数的真值为负数,则
X=[X]补-2^8=11110011-100000000=-1101
仔细观察发现,由负数的原码求补码的简便方法,即“原码除符号位以外,按位取反,末位加1得补码”的方法也可用来由补码求原码,“补码除符号位以外,按位取反,末位加1得原码”。如上例,[X]补=11110011,[X]补除符号位全部取反,得10001100,末位加1,得[X]原=10001101,最终X=-1101.
实际上,引入补码的概念就是为了消除减法运算,但是根据补码的定义,在计算补码的过程中也出现了减法,例如,当X=-10110时,在机器字长为8位的计算机中,[X]补=100000000-10110=11101010,所以,计算机中所有对负数的真值求补码的过程,都是使用的简便方法来实现的。
4.反码表示法
反码通常是用来作为由原码求补码或者由补码求原码的中间结果。二进制数X的反码定义如下:
设机器字长为n,若X是纯整数,则
[X]反=X 0<=X<=2^n-1 -1
[X]反=2^n-1+X -(2^n-1-1)<=X<=0
若X是纯小数,则
[X]反=X 0<=X<1
[X]反=2-2^-(n-1)+X -1<X<=0
【例】设机器字长为8位,计算二进制数+110和-110的反码。
根据反码表示法定义,得
[+110]反=00000110
[-110]反=2^8-1+(-110)=11111111-110=11111001
从定义可以看出,正数的反码就是其本身。负数的补码是其原码符号位不变,其它数值位按位取反得到的。如上例中,[-110]原=100000110,除符号位,其他数值位按位取反可得[-110]反=11111001.实际上计算机中就是通过这种方式获取的反码。
【例】设机器字长为8位,计算0的反码。
当真值X=0时,符号位可以为“+”也可以为“-”:
当符号位为正时,[X]反=[X]原=[+0]原=00000000
当符号位为负时,[X]原=[-0]原=10000000,根据简便算法按位取反,得[X]反=11111111
可见,和原码一样,若用反码表示机器数,0的表示不唯一,会出现+0和-0两种情况。进一步分析可知,整数的反码表示范围与原码相同,也是-(2^n-1 -1)~(2^n-1 -1),其中n为机器字长。
最后,总结发现,正数的原码、反码、补码都相同。而在计算机中,负数的反码由原码除符号位保持不变,其他数值位取反得到的,而补码是由反码的值末位加1取得的。以负数整数为例:
如果
[X]原=XnXn-1Xn-2……X0
那么
[X]反=Xn X[反]n-1 X[反]n-2 X[反]0
[X]反=Xn X[反]n-1 X[反]n-2 X[反]0 +1
至此我们已经介绍了有符号数的3中机器数的编码方式。目前,在计算机中,广泛使用的是补码和原码表示法,反码表示法一般作为原码和补码转换的中间结果。
5.移码表示法
当用补码表示法来表示一个有符号数时,由于符号位的存在,人们很难直观地看出不同补码所表示的真值的大小关系。由于负数的符号位为1,与人们习惯上的表示法不同,很容易判断错误。例如下面几个数值:
十进制20对应的二进制数真值为+10100,补码形式为:010100.
十进制数-20对应的二进制数真值为-10100,补码形式为:101100.
十进制数-21对应的二进制数真值为-10101,补码形式为:101011.
十进制数+21对应的二进制数真值为+10101,补码形式为:010101.
但从补码形式上看,区分这几个补码表示的真值的大小,很难直关说出,需要进行一番判断。为此提出了移码的表示。
移码的定义如下:
[X]移=2^n+x (-2^n<=X<2^n)
式中X为真值,n为纯整数的位数。
以纯整数为例,也就是说真值X的移码等于真值加上一个2^n(n为整数真值的位数),再看一下上面例子中这几个整数的移码的表现形式:
十进制数20对应的二进制真值为+10100,移码形式为:100000+10100=110100.
十进制数-20对应的二进制数真值为-10100,移码形式为:100000+(-10100)=001100.
十进制数-21对应的二进制数真值为-10101,移码形式为:100000+(-10101)=001011.
十进制数+21对应的二进制数真值为+10101,移码形式为:100000+10101=110101.
比较这4个十进制整数的移码,可以直接从移码的形式比较出:110101>110100>001100>001011,与真值的实际大小关系一致。
当X=0时,二进制真值可以表示为+0000和-0000.
[+0000]移=10000+0000=10000
[-0000]移=10000-0000=10000
可见,使用移码表示法,0也是唯一的。
思考下面这个问题:虽然引入移码使比较真值大小更直观,但计算机中的数值很多情况下都是使用补码形式存储的,如何在移码和补码之间快速转换?如果不能快速在移码和补码之间进行快速转换,那么移码的引入反而也会带来很多麻烦。
观察发现,同一个真值的移码和补码的编码,仅仅符号位的取值相反,其它位都相同。也就是说,如果已知补码,不管正负数,只要将其补码的符号位取反即可得到移码。真值、移码、补码的关系如下:
真值X(十进制) | 原码[X]原 | 补码[X]补 | 移码[X]移 |
-127 | 11111111 | 10000001 | 00000001 |
-126 | 11111110 | 10000010 | 00000010 |
-125 | 11111101 | 10000011 | 00000011 |
-1 | 10000001 | 11111111 | 01111111 |
0 |
00000000 100000000 |
00000000 | 10000000 |
+1 | 00000001 | 00000001 | 10000001 |
+125 | 01111101 | 01111101 | 11111101 |
+126 | 01111110 | 01111110 | 11111110 |
+127 | 01111111 | 01111111 | 11111111 |
…… | …… | …… | …… |
来源:https://blog.csdn.net/mez_Blog/article/details/102666146