《深入理解计算机系统》学习(7):信息存储、整数表示和运算

三种最重要的数字表示
(1)无符号编码基于传统的二进制表示法,表示大于或者等于零的数字。
(2)补码编码是表示有符号整数的最常见的方式,有符号整数就是可以为正或者负的数字。
(3)浮点数编码是表示实数的科学计数法的以2为基数的版本。

一、信息存储

大多数计算机使用8位的块,或者字节,作为最小的可寻址的内存单位,而不是访问内存中单独的位。机器级程序将内存视为一个非常大的字节数组,称为虚拟内存。内存的每个字节都由一个唯一的数字来标识,称为该字节的地址。虚拟地址空间只是一个展现给机器级程序的概念性映像,实际上是将动态随机访问存储器、闪存、磁盘存储器、特殊硬件和操作系统软件结合起来,为程序提供一个统一的字节数组。

1.1 十六进制表示法

一个字节由8位组成。二进制表示法中的值域是:00000000 ~ 11111111,十进制表示法中的值域是:0 ~ 255,二进制表示法太冗余,十进制和位模式的相互转化很麻烦。替代的方法是以16为基数来表示位模式。

给定一个十六进制数,可以通过展开每个十六进制数字,将其转换为二进制格式。反过来,给定一个二进制数字,通过划分4位一组来转换位十六进制,如果位总数不是4的倍数,在最左边补0。

1.2 字数据大小

每台计算机都有一个字长,指明指针数据的标称大小。因为虚拟地址是以这样的一个字来编码的,所以字长决定的最重要的系统参数就是虚拟地址空间的最大大小。也就是说,对于一个字长为w位的机器而言,虚拟地址的范围为0 ~ 2W - 1,程序最多访问2W个字节。

32位字长机器限制虚拟地址空间为4千兆字节(写作4GB),即232字节。扩展到64位字长使得虚拟空间为16EB。大多数64位机器也可以运行为32位机器编译的程序,这是一种向后兼容。
请添加图片描述

1.3 寻址和字节顺序

在几乎所有的机器上,多字节对象都被存储为连续的字节序列,对象的地址为所使用字节中最小的地址。假设一个类型为int的变量x的地址为0x100,即&x的值为0x100,那么x的四个字节将被存储在内存的0x100、0x101、0x102和0x103位置。

排列表示一个对象的字节有两个通用的规则,假设x的十六进制值为0x01234567,地址范围0x100~0x103的字节顺序依赖于机器的类型。最高有效字节(0x01)在前面的方式为大端法,最低有效字节(0x67)在前面的方法为小端法。
请添加图片描述
大多数Intel兼容机都只用小端模式。许多比较新的处理器是双端法,可以配置为大端或者小端的机器运行。然而,一旦选择了特定的操作系统,也就确定了字节顺序。比如,许多移动电话的ARM微处理器,其硬件可以按小端或大端两种模式操作,但是这些芯片上最常见的两种操作系统,Android和IOS只能运行于小端模式。

对于大部分业务场景,机器使用的字节顺序是完全不可见的,不同类型的机器编译的程序都会得到同样的结果。不过在一些情况下需要关注字节顺序的问题。
(1)在不同类型的机器之间通过网络传送二进制数据时,一个常见的问题是当小端机器产生的数据被发送到大端机器,或者相反,接受程序会发现数据反序。为了避免这类问题,网络应用程序的代码编写必须遵守已建立的关于字节顺序的规则。
(2)检查机器级程序时,阅读表示整数数据的字节序列。在小端模式中,整数值0x200b43在机器代码中为相反的字节顺序43 0b 20 00。
(3)编写规避正常的类型系统的程序,如通过使用强制类型转换或联合来允许一种数据类型引用一个对象,而该类型与创建这个对象时定义的数据类型不同。

1.4 表示字符串

C语言中字符串被编码为一个以null(其值为0)字符结尾的字符数组。每个字符都有某个标准编码(如ASCII)来表示。“12345”的字节序列为31 32 33 34 35 00,0x00表示终止字节。在使用ASCII码作为字符码的任何系统上都将得到相同的结果,与字节顺序和字大小规则无关(作为字符数组存储,而字符是单字节的,不需要考虑字节顺序)。因此,文本数据比二进制数据具有更强的平台独立性。

指令编码是不同的,不同的机器类型使用不同的且不兼容的指令和编码方式,即使是完全一样的程序,运行在不同的操作系统上也会有不同的编码规则,因此,二进制代码是不兼容的,二进制代码很少能在不同机器和操作系统组合之间移植。

1.5 布尔代数简介

1950年前后乔治·布尔注意到通过将逻辑值TRUE和FALSE编码为二进制值1和0,能够设计出一种代数,以研究逻辑推理的基本原则。最简单的布尔代数是在二元集合{0,1}基础上的定义。布尔运算&、|、~、^对应逻辑运算与、或、非和异或。可以将4个布尔运算扩展到位向量的运算,位向量就是由0和1组成的串。

位向量的一个应用就是表示有限集合。例如,a = [01101001] 表示集合A = {0,3,5,6},而b = [01010101] 表示集合B = {0,2,4,6}。使用这种编码集合的方法,布尔运算|和&分别对应于集合的并和交,而~对应于集合的补。

位级运算的一个常见用法就是实现掩码运算,这里掩码是一个位模式,表示从一个字中选出的位的集合。位级运算x&0xFF生成一个由x的最低有效字节组成的值,而其它字节就被置为0。

C语言还提供了一组移位运算,向左或者向右移动位模式。左移运算x<<k会使x向左移动k位,丢弃最高的k位,并在右端补k个0。右移运算支持两种形式的右移:逻辑右移和算术右移。逻辑右移在左端补k个0,算术右移在左端补k个最高有效位的值。

C语言标准没有明确定义对于有符号数应该使用哪种类型的右移,但是几乎所有的编译器/机器组合都对有符号数使用算术右移,且许多程序员也都假设机器会使用这种右移。另一方面,对于无符号数,右移必须是逻辑的。

二、整数表示

2.1 整型数据类型

C语言支持多种整型数据类型,表示有限范围的整数。根据类型的字节分配,不同的大小所能表示的值的范围是不同的。几乎所有的整型类型在不同的机器(32位和64位)上分配的字节数是相同的,除了long,在64位机器上使用8个字节表示,在32位机器上使用4个字节表示。

2.2 有符号和无符号之间的转换

(1)无符号数编码
每一个长度为w的位向量都能映射为0 ~ 2w-1之间的唯一值。例如,长度为8的unsigned char能表示0 ~ 255。
(2)有符号数编码
有符号整型数据能够表示的取值范围是不对称的,负数的范围比整数的范围大1。因为常见的有符号数的计算机表示方式就是补码形式,将最高有效位解释为负权。
请添加图片描述
B2T4为从位向量到整数的映射。可以看出,位向量0000 0000 ~ 0111 1111表示0 ~ 127,而位向量1000 0000 ~ 1111 0000表示-128 ~ -1。

C语言允许使用 (type) x 在不同的数字数据类型之间做强制类型转换,转换结果保持位值不变,只是改变了解释这些位的方式。例如,有符号的四位整数-1强制转换为无符号的15,两者的位向量是相同的,都是1111,只不过对最高位的解释不同。

可以看出,只有部分无符号整数(0~2w-1 - 1)转有符号时能得到数值相同的结果。

2.3 不同字长之间的转换

无符号和有符号之间的转换讨论的是相同字长的整数。在不同字长的整数之间也存在转换,同时又保持数值不变。当数据类型太小以至于不能表示想要的值时,这是不可能的,从一个较小的数据类型转换为较大的类型,总是可能的。
请添加图片描述
(1)从一个无符号数转换为更大的数据类型,只要简单地在表示地开头添加0,这种拓展被称为零拓展。

例如,16位的unsigned short类型53191,为0x CF C7,转换为32位的unsigned int类型,为0x 00 00 CF C7

(2)从一个有符号数转换为一个更大的数据类型,可以执行一个符号拓展,在表示中添加最高有效位的值。

例如,16位的short类型-12345,为0x CF C7,转换为32位的int类型,为0x FF FF CF C7
16位时,-12345 = -1* 215 + (x14* 214 + …… + x0* 20),xi 为对应的位(0或1);
32位时,-12345 = -1 * 231 + (1 * 230 + …… + 1 * 215) + (x14* 214 + …… + x0* 20),
可以知道-1* 215 和 -1 * 231 + (1 * 230 + …… + 1 * 215) 相等,其余部分相同,所以转换前后数值大小一样。

2.4 截断数字

当将int类型的x转换为short类型,32位的53191(0000 0000 0000 0000 1100 1111 1100 0111)截断为16位的-12345(1100 1111 1100 0111)。
请添加图片描述
(1)截断无符号数
对于一个无符号数,截断后的数值就是丢弃高n位的结果。

(2)截断有符号数
对于一个有符号数,截断后的数值是丢弃高n位的结果后再将最高位转换为符号位。

例如,32位的53191(0000 0000 0000 0000 1100 1111 1100 0111)截断为16位的-12345(1100 1111 1100 0111)。第16位的1变为符号位,截断后的值为丢弃高n位的结果(1100 1111 1100 0111,无符号位)减去 215 (100 1111 1100 0111,无符号位),再减去 215(1100 1111 1100 0111,最高位1作为符号位),也就是53191 - 215 - 215 = 53191 - 216 = 53191 - 65536 = -12345。

三 整数运算

3.1 无符号加法

长度为w的位向量能表示无符号整数为0~2w,两个范围内的无符号整数相加,和的范围为[0,2w+1 - 2],可能需要w+1位来表示。
请添加图片描述
正常情况下,x+y的值保持不变,而溢出情况则是该和数减去2w的结果。例如,4位表示的9(1001)、12(1100)的和为21(10101),丢弃最高位后得到5(0101),即21-16=5。

3.2 补码加法

长度为w的位向量能表示有符号整数为-2w-1~2w-1-1,两个范围内的无符号整数相加,和的范围为[-2w,2w - 2],可能需要w+1位来表示。
请添加图片描述
正溢出时,截断的结果是从和数中减去了2w,负溢出时,截断的结果是把和数加上2w

例如,4位表示的-8(1000)、-5(1011)的和为-13(10011),丢弃最高位后得到3(0011),即-13+16=3。负溢出时最高位一定为1,第二位一定为0,丢弃最高位1(加2w)并将0转为符号位(无影响)。原来-13 = -1 * 24 + 21 + 20,加上25后得到去掉最高位后的值3 = 0 * 23 + 21 + 20

4位表示的5(0101)、5(0101)的和为10(01010),丢弃最高位后得到-6(1010),即10-16=-6。正溢出时最高位一定为0,第二位一定为1,丢弃最高位0(无影响)并将1转为符号位(减2w-1加-1*2w-1,即减2w)。原来10 = 0 * 24 + 23 + 21 ,减去24后得到去掉最高位后的值-6 = -1 * 23 + 21

3.3 无符号乘法

范围在[0,2w -1]的整数x和y可以被表示为w位的无符号数,乘积x*y的取值范围为[0,22w -2w+1+1],可能需要2w位来表示,此时,C语言中的无符号乘法结果用2w位的整数乘积的低w位表示。

3.4 补码乘法

范围在[-2w-1 ,2w-1 -1]的整数x和y可以被表示为w位的补码数字,乘积x*y的取值范围为[-22w-2+2w-1, -22w-2],可能需要2w位来表示,此时,C语言中的无符号乘法结果先将2w的乘积截断为w位,再将最高位转为符号位。

3.5 乘以常数

以往的大多数机器上整数乘法指令执行很慢,需要10个或者更多的时钟周期,然而其它整数运算(加法、减法、位级运算、移位)只需要1个时钟周期。即使在Intel Core i7 Haswell上,其整数乘法也需要3个时钟周期。因此,编译器尝试用移位和加法运算的组合来优化代替乘以常数因子的乘法。

左移一个数值等价于执行一个与2的幂相乘的无符号乘法。假设x为位模式[xw-1,xw-2,…,x0]表示的无符号整数。左移k位后的整数为[xw-1,xw-2,…,x0,0,…,0],从x0到xw-1的每一位左移k位后的结果即在原来的结果上乘以2k,右边添加的0对数值无影响。

由于整数乘法比移位和加法的代价要大得多。许多C语言编译器试图以移位、加法、减法的组合来消除整数乘以常数的情况,通过将乘法指令拆分为多个与2的幂的乘积的和,从而优化为移位、加法、减法的组合。例如,x*14,利用14=23+22+21,编译器会将乘法重写为(x<<3)+(x<<2)+(x<<1),将一个乘法替换为三个移位和两个加法。当然,是使用组合优化还是乘法指令。取决于两者的相对速度,而这与机器相关,大多数编译器只在需要少量移位、加法、减法就足够的时候才使用这种优化。

3.6 除以2的幂

在大多数机器上,整数除法比整数乘法更慢,需要30个或者更多的时钟周期。除以2的幂也可以用移位运算来实现,无符号和补码数分别使用逻辑移位和算术移位来达到目的。

(1)除以2的幂的无符号除法
对位向量[xw-1,xw-2,…,x0]逻辑右移k位会得到位向量[0,…,0,xw-1,xw-2,…,xk]。右边的位被舍弃后的位向量为原数值除以2k的取整结果。如果舍弃的位中包含1,则不取整结果会是小数(小数部分为舍弃位除以2k的结果)。
请添加图片描述

(2)除以2的幂的补码除法
对于非负数,变量x的最高有效位为0,效果和逻辑右移一样。

当x为负数,对位向量[xw-1,xw-2,…,x0]算术右移k位会得到位向量[xw-1,…,xw-1,xw-1,xw-2,…,x0],x为负数时xw-1为1。假设原位向量中非符号位的值为Y,则总体值为(-1)*2w-1+Y,右移k位后值应为(-1)*2w-1/2k+Y/2k,后者即原非符号位部分右移k位的结果,前者则通过左侧的k+1个xw-1得到。
请添加图片描述

  • 27
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值