由于计算机只能直接识别和处理用0、1两种状态表示的二进制形式的数据,所以在计算机中无法按人们日常的书写习惯用正负号、绝对值来表示数值。像表示数字一样,需要用二进制代码0和1来表示正负号。这样,在计算机中表示带符号的数值数据时,符号和数据均采用0、1进行了代码化。这种采用二进制表示形式的连同符号一起代码化了的数据,在计算机中统称为机器数或机器码。而与机器数对应的用正、负符号加绝对值来表示的实际数值称为数的真值。机器数可分为无符号数和带符号数两种。无符号数是指计算机字长的所有二进制位均表示数值;带符号数是指机器数分为符号和数值部分,且均用二进制代码表示。
整数是没有小数部分的整型数字。例如,123是整数而1.23不是。整数可以被当作小数点位置固定的数字:小数点固定在最右边。因此,定点表示法用于存储整数。在这种表示法中,小数点是假定的且并不存储。为了更有效地利用计算机内存,无符号和有符号整数在计算机中的存储方式不同。
无符号整数无符号整数在计算机中的应用非常广泛。例如,我们希望限定一个任务的执行次数,使用一个无符号整数即可,用它来记录该任务还有多少次执行次数。另外,我们还可以使用无符号整数表示不同内存单元的地址,这与生活中的用法很相似,如第129 号信箱或第131 号信箱。我们可以将一个无符号整数表示为一连串的二进制数字序列。有符号整数在实际算术运算中存在大量的负数。我们可以将2k个位中,专门用一个位表示正负符号,这样实际上将数一分为二,一半表示正数,另一半表示负数。这种方法称为符号位表示法,就是我们后面提到的原码表示法。第二种思路也是早期计算机所采用的方法:将一个正数的所有位全部取反,即得到该正数所对应的负数编码,例如“+ 5”表示为“00101”,那么“-5”则为“11010”,我们称之为反码表示法。不同的编码方法会导致不同的加法器逻辑复杂度。前面提到的两种编码方法,符号位表示法和反码表示法,在硬件逻辑设计上都相当复杂。例如,对符号相反的两个数求和,加法器不能对它们直接逐位相加。因此,需要设计更适合硬件操作的编码方案,那就是补码表示法。几乎目前所有的计算机都采用这种编码方式。原码、反码和补码数值有正负之分,所谓原码是用一个数的最高位存放符号(0为正,1为负),后续其他位与数的真值相同。假设机器能处理的位数为8,即字长为1个8位字节,原码能表示数值的范围为 (-127 ~ 0,0 ~ 127)。请注意原码不等同源码。这里的原码是编码中的专用术语,而“源码”通常是指程序的源代码。用原码这种数值表示方法可以对数进行算术运算,用带符号位的原码进行乘除运算时结果正确,但是在进行加减运算的时候就会出现问题。例如,字长为8位的原码,计算机把减法当作一个正数和一个负数的加法来计算。例如,十进制的计算(1-1=?):(1)10-(1)10 = (1)10 +(-1)10 =(0)10,而(00000001)原 + (10000001)原 = (10000010)原 = (-2) 显然不正确。正数的反码与原码相同,负数的反码表示法是用最高位存放符号,并将原码的其余各位逐位取反。反码的取值空间和原码相同且一一对应。下面是反码的减法运算:例如,对于十进制数:(1)10 - (2)10 =(1)10+(-2 )10 =(-1)10,(00000001)反+(11111101)反= (11111110)反 = (-1) 正确。然而对于:(1)10 -(1)10=(1)10+(-1)10=(0)10用反码进行计算:(00000001)反+ (11111110)反=(11111111)反 =(-0)可以看到,将计算的结果取其反码后会出现有(-0)。问题出现在(+0)和(-0)上,在人们的计算概念中零是没有正负之分的,于是就引入了补码概念。在补码表示法中,正数的补码表示与原码相同,即最高符号位用0表示正,其余位为数值位。而负数的补码则为它的反码,并在最低有效位(即D0位)加1所形成。当x为正数时,x= +x1x2…xn时:[x]原=[x]反=[x]补=0x1x2…xn当x为负数时,即x=-x1x2…xn时:[x]原=1x1x2…xn[x]反=1x1?x2…xn[x]补=1x1?x2…xn+1式中xi表示xi的反。在二进制补码表示法中,最左位决定符号。如果它是0,该整数为正;如果是1,该整数为负。处理器内部默认采用补码表示符号数,补码的表示范围与原码相同。补码的加减运算示例如下:(1)10-(1)10=(1)10+(-1)10 =(0)10(00000001)补 + (11111111)补 =(00000000)补= (0)正确。(1)10-(2)10=(1)10+(-2)10 =(-1)10(00000001)补+ (11111110)补= (11111111)补 = (-1)正确。补码的设计目的是:1)使符号位能与有效值部分一起参加运算,从而简化运算规则。2)使减法运算转换为加法运算,进一步简化计算机中运算器的线路设计。所有这些转换都是在计算机的最底层进行的,我们在使用汇编、C等程序设计语言中使用的是原码,而数据在计算机中是以补码形式存在的。补码的运算规则:[X+Y]补= [X]补+[Y]补 [X-Y]补= [X]补+[-Y]补若已知[Y]补,求[-Y]补的方法是:将[Y]补的各位(包括符号位)逐位取反再在最低位加1即可。例如,[Y]补= 101101,[-Y]补= 010011。【例1?5】 求105和-105的二进制补码。解:正数的补码不变,负数进行补码运算。105的二进制原码 0 1 1 0 1 0 0 1105的二进制补码 0 1 1 0 1 0 0 1(正数补码不变)-105的二进制反码 1 0 0 1 0 1 1 0(符号位不变,数值逐位取反)-105的二进制补码 1 0 0 1 0 1 1 1(反码加1)【例1?6】 将用二进制补码表示法存储在8-bit存储单元中的11100000还原成整数原值。解:最左位是1,因此符号为负。在整数转换为十进制前进行补码运算。
1 1 1 0 0 0 0 0 进行补码运算: 1 0 0 1 1 1 1 1+1(取反加1) 得到二进制数: 1 0 1 0 0 0 0 0 整数转换为十进制 3 2 加上符号得到原值 -3 2 原码、反码和补码之间的转换原码、反码和补码这三种编码既有相同点,又有各自的不同,主要体现在以下几个方面:1)正数的3种编码都等于真值本身,而负数各不相同。2)符号位都在最高位,补码和反码的符号位可作为数值位的一部分看待,与数值位一起参加运算,但是原码的符号位不允许和数值位一样看待,需要分开处理。3)真值零的原码和反码都有两种不同的表示形式,而补码的表示形式只有一种。从表示范围来看,原码和反码的表示范围是对称的,而补码负数表示范围比正数表示范围多一个数,表示定点小数时最小的值是-1,表示定点整数时最小的值是-2n。因为正数的原码、反码和补码的表示形式是相同的,而负数则各不相同,所以3种码制之间的相互转换实际上就是其负数形式的转换。1)将反码表示的数据转换成原码。转换方法:负数的符号位保持不变,数值部分逐位取反。2)将补码表示的数据转换成原码。转换方法:利用互补的道理对补码再次求补即得到原码。3)将原码表示的数据转换成补码。转换方法:负数的符号位保持不变,数值部分逐位取反后,最低位加1便得到负数的补码。
溢出因为存储空间大小(即存储单元的位的数量)的限制,可以表达的整数范围是有限的。在n-bit存储单元中,我们可以存储的无符号整数仅为0到2n-1之间。例如,我们保存整数11到一个仅为4位大小的存储单元中,不会出现问题。但是又试图再加上9,就发生了溢出的情况:表示十进制数20的最小位数是5位,也叫是说20=(10100)2,所以计算机丢掉最左边的位,并保留最右边的4位(0100)2。最终人们看到新的整数显示为4而不是20。实数是带有整数部分和小数部分的数字,用于维持正确度或精度的解决方法是使用浮点表示法。该表示法允许小数点浮动:我们可以在小数点的左右有不同数量的数码。实数一般用浮点数表示,因为它的小数点位置不固定,所以称浮点数。它是既有整数又有小数的数,纯小数可以看作实数的特例。浮点表示法在科学中用于表示很小或很大的十进制数。在称作科学计数法的表示法中,定点部分在小数点左边只有1个数码而且位移量是10的幂次。例如:57.625、-1 984.045、0.004 56都是实数,以上三个数又可以表示为:57.625=5.762 5×101-1 984.045=-1.984 045×1030.004 56=4.56×10-3
规范化为了使表示法的固定部分统一,科学计数法(用于十进制)和浮点表示法(用于二进制)都在小数点左边使用了唯一的非零数码。这称为规范化。十进制系统中的数码可能是1到9,而二进制系统中该数码是1。在下面,d是非零数码,x是一个数码,y是0或1。十进制 → ± d.xxxxxxxxxxxxxx 注意:d是1到9,每个x是0到9二进制 → ± 1.yyyyyyyyyyyyyy 注意:每个y是0或1
符号、指数和尾数在一个数规范化之后,计算机中只存储了一个数的三部分信息:符号、指数和尾数(小数点右边的位)。小数点和定点部分左边的位1并没有存储——它们是隐含的。例如,+1000111.0101规范化后变为:
26 × 1.00011101016 0001110101
↑ ↑ ↑符号 指数 尾数
符号:一个数的符号可以用一个二进制位来存储(0或者1)。指数:指数(2的幂)定义为小数点移动的位数。注意幂可以为正也可以为负。余码表示法(后面讨论)是用来存储指数位的方法。尾数:尾数是指小数点右边的二进制数。它定义了该数的精度。尾数是作为无符号整数存储的。如果我们把尾数和符号一起考虑,我们可以说这个组合是作为符号加绝对值格式的整数存储的。但是,我们需要记住它不是整数——而是像整数那样存储的小数部分。我们强调这点是因为在尾数中,如果我们在数字的右边插入多余的零,这个值将会改变,而在一个真正的整数中,如果我们在数字的左边插入多余的零,这个值是不会改变的。3.余码表示法为了让正的和负的整数都可以作为无符号数存储,计算机通常采用余码表示法。在余码系统中,使用一个正整数(称为一个偏移量)加到每个数字中,用于把它们统一移到非负的一边。这个偏移量的值是2m-1-1,m是内存单元存储指数的大小。例如,我们可以用3位存储单元在数字系统中表示8个整数。使用一个单元作为0,分开其他7个(不等地)我们可以在-3 ~ 4的范围中表示整数(此时有正负数)。在该范围中增加3个单位到每个整数中,我们可以统一把所有整数向右移,使其均为正整数而无须改变这些整数的相对位置,避免了相互调整。新系统称为余3码系统,或者偏移量为3的偏移表示法。这种新的表示法与移位前的表示法相比,其优点在于余码系统中的所有整数都是正数,当我们在这些整数上进行比较或运算时不需要考虑符号。对于3位存储单元,如我们希望那样,偏移量是23-1-1=3。
算术运算包括加、减、乘、除等,适用于整数和浮点数。
整数的算术运算首先我们集中讲整数的算术运算。所有类似于加、减、乘、除等的算术运算均适用于整数,我们先研究加和减,乘法运算可以在软件中通过连加的方法或在硬件中通过其他技术实现,除法运算可以在软件中通过连减的方法或在硬件中通过其他技术执行。对整数的所有形式都可以进行加和减的运算。不过我们给出二进制补码形式,因为现在在计算机中整数只能以补码形式存储。二进制补码中加法就像十进制中的加法一样:列与列相加,如果有进位,就加到下一列上。但是在二进制数加法中,当两位相加时结果只能是0或1。在加法中,得到一个1的进位需要进到下一列上。现在我们可以定义二进制补码中两个整数相加的法则。二进制补码中两个整数相加的法则:2个位相加,将进位加到下一列。如果最左边的列相加后还有进位,则舍弃它。【例1?7】 用二进制补码表示方法计算17加22。解:(+17)+(+22)=(+39)这些数字在8位存储单元中用二进制补码分别表示为00010001和00010110。结果对于任何分配大小来说是类似的。
进位 1 0 0 0 1 0 0 0 1+ 0 0 0 1 0 1 1 0 结果 0 0 1 0 0 1 1 1结果是十进制数39。【例1?8】 请按补码形式计算24加-17。解:(+24)+(-17)=(+7)这两个数的补码可按如下描述运算:
进位 1 1 1 1 1 0 0 0 1 1 0 0 0+ 1 1 1 0 1 1 1 1 结果 0 0 0 0 0 1 1 1结果是+7,注意最后的进位被舍去(从行的最左侧算起)。【例1?9】 请按补码形式计算-35加20。解:(-35)+(+20)=(-15)这两个数的补码可按如下描述运算:
进位 1 1 1 1 1 0 1 1 1 0 1 + 0 0 0 1 0 1 0 0 结果 1 1 1 1 0 0 0 1结果是-15。
浮点数的算术运算浮点数(实数)也可以进行包括加、减、乘、除在内的算术运算。我们只介绍加法和减法,因为乘法和除法是加法和减法的多次重复运算。当被IEEE标准格式规范化之后浮点型加减法变得详尽而又复杂,我们在这里并不对其所有细节和特例进行讲解,而只是给出总体概述。浮点数加减法是同一个处理过程,步骤如下:检验符号,如果符号相同,相加其值,结果符号与它们相同。如果符号不同,比较绝对值,绝对值大的减去小的,结果符号取绝对值大的一方。移动小数点,使两者阶数相同。也就是说,当阶数不同时,数值小的一方将小数点左移,但要使值不变。将变换后的数值进行加减运算(包括整数和小数部分)。