0%

计算机的数值表示和运算

进制转换

我们都知道计算机底层数值都是二进制的(由高低电平表示)。而我们生活中常用十进制,同时一些场合下也会用16进制。因此首先我们需要知道进制之间如何转换。

二进制转十六进制

二进制数以小数点为界,整数部分从小数点从左到右数,每4位分为一组,不足四位的时候在左端补0;小数部分从小数点从右往左数,每四位为一组,不足四位则在右端补0。然后每组按照二进制数与十六进制数对应关系转换。因为2的四次方为16,所以十六进制的每单个数都可以用四位二进制数表示出来。

十六进制转二进制

直接每位十六进制数都对应转换为四位二进制数即可。

二进制转十进制

十进制转二进制

十进制整数

十进制数不断除二,记录下每一次的余数,直到商为0。先得到的余数是二进制中的低位,后得到的是二进制整数中的高位。

十进制小数

小数位不断乘二,记录下每一次结果的整数位(当然只有0或1),直到小数位为0。先得到的整数位是二进制中的高位,后得到的整数位是二进制中的低位。

因此记录下来的二进制数为0.0101。即

当然存在无法达到小数位为0的情况。视精度进行取舍,假如要求五位精度,求到第六位看第六位是0还是1,如果是0直接舍去,如果是1则在第五位进位。

补码制表示二进制数

上面我们讨论的二进制数都是无符号数,也就是无法用来表示这个数的正负。容易得知无符号数的表示范围是

我们思考一种简单的标志正负的方式,如果用n位来表示一个数,那么我们用最高位来标志这个数的正负,一般最高位为0代表正数,最高位为1代表负数。此时最高位我们称为符号位,剩余的n-1位称为数值位。带有符号的数我们称之为符号数

考虑此时的n位二进制数的表示范围,以四位为例,对正数而言最大为0111,负数的表示范围为1111(-8)到1001(-1),此时0有1000和0000两种表示方法。也就是表示范围为

但是这样做会增加运算逻辑的复杂程度,因为加法器要先检查操作,然后再检查二者的符号:符号相同则将二者数值位相加然后赋给结果相同的符号位;如果符号不同还需要比较二者数值部分的大小,用较大的减去较小的,然后赋给结果较大的操作数的符号。

因此我们引进补码制来表示符号数。实际中的计算机符号数都是用补码制表示的。所以以后符号数就等同于补码制二进制数。

补码运算

我们先来了解一下补码:一个n位二进制数D的补码为

求补码的运算我们称之为补码运算。

补码运算可以变形

而$2^n-1-D$实际就是n位全1二进制数减去D,相当于对D取反。也就是说求D的补码的过程实际可以变成求D的补码然后再加一。这个运算过程对计算机硬件来说比减法容易得多。

补码制表示规则和性质

在补码制下同样用最高位表示符号,且0为正,1为负。

十进制数3的四位补码制二进制数同样为0011,而补码制下的负数为正数的补码。也就是-3表示为

如果你对补码制下的负数再进行补码运算,会得到该数的相反数,也就是变回正数。

这使得在补码制下正数与负数的相互转换非常方便,一对相反数互为对方的补码。

考虑n位补码制二进制数的表示范围,以4位为例:

正数最大为0111

负数可以从 1000(-8)~1111(-1)注意和非补码制的区别

0为0000,且0的补码仍为0000。

也就是

补码制下的拓展

对于无符号数,由于没有符号,从4位二进制数拓展到8位只需要在左端加四个0。称这种拓展方式位零拓展

对于符号数,我们需要考虑它的符号,拓展的规则是直接按照它的符号位进行拓展,也就是1101拓展到8位就表示为了11111101。

我们可以求一下拓展前后的补码验证一下,前者的补码为0011,也就是3,后者为00000011,也是3。

对符号数的位数拓展方式我们称为符号拓展

运算

加减

补码制的提出是为了简化运算。在补码制下运算不需要考虑符号位,符号位直接参与运算即可。并且如果操作为减法,那么就对后面的操作数求补码然后按加法运算即可。而且如果最高位产生了进位直接舍去最高位进位即可。

溢出

运算正确的结果超出了给定的取值范围,就是溢出。这里给出的取值范围就是n位二进制补码表示的范围。

最高位进位和溢出没有直接关系,可以证明异号相加不会溢出,同号相加才有可能溢出。

正确结果10超出了四位符号数的表示范围。

判断溢出的方法很简单,如果两个相同符号的数相加,运算结果的符号位变化,那么就可以判断产生了溢出。

乘除

逻辑运算

移位

总位数不变

左移:右端直接补0,1110左移两位—1000

逻辑右移:左端直接补0,1110逻辑右移两位—0011

算术右移:左端根据符号位补,1110算术右移—1111

左移和算术右移本质为乘2的n次方(不丢有效位1的情况下)

按位运算

浮点数的表示和运算

我们现在开始考虑如何来表示小数。

首先确定正负,我们还是取最高位为符号位,值用S表示。

先不考虑小数点在哪里,先将这个值用无符号数的方式表示出来,这个过程中我们需要确定我们要取多少位,也就是有效长度。

假设我们取a位有效长度,然后用a位的空间存储这个有效值A。

最后我们通过移位的方式来确定小数点在哪里,实现方法是在后面用n位的长度用补码的方式存一个阶数,小数点从有效值的最右端根据阶数的值E移位。

根据标准二进制浮点数运算标准IEEE-754,移位应该进行到有效值的最高位的后一位,也就是有效值那里的实际值永远是1.x,也就是在该规则方式下,我们用

来表示带有小数部分的数。x我们称为尾数部分。

在实际的存储中,如果尾数部分长度为m,那么a=m+1,因为1是公有的所以不需要特别去表示它,直接表示x的部分就行了,也因此存在一个隐藏位1。

实际中我们需要经常比较阶数部分,为了方便比较,阶数部分实际存储的值都是阶数E+2^(n-1)-1,阶数部分存储的值我们称为阶码,其范围就变成了[000···000, 111···111],实际表示的范围[-2^(n-1)+1,2^(n-1)],这样比较两个浮点数的阶数就很方便了,直接比较的是阶码也就是无符号二进制数。

根据浮点数标准IEEE-754,有两种浮点数,即单精度浮点数和双精度浮点数。

二者的符号位都是1位。单精度的阶码位数有8位,尾数有23位;双精度阶码有11位,尾数有52位。

几个特殊值

浮点数0:尾数全为0,阶码全为0

无穷:尾数全为0,阶码全为1

无效操作数:尾数不全为0,但阶码全为1

因此实际储存中为了正常表示非0浮点数,阶码不能全为1或者0。

浮点数运算

  1. 移位使得两个数的阶码相等,小阶去对等大阶,不要忘记了隐藏位1。
  2. 尾数作运算,为了对应可能产生的进位,需要把隐藏位拓展为两位01。
  3. 将隐藏位恢复为1,化为标准的浮点数

如果阶码减小到全为0,说明结果的指数太小,出现下溢;如果运算后阶码全变成1,说明结果的指数太大出现上溢。