《CSAPP》 Review 2: 数据

405 阅读6分钟

32位/64位

首先考虑一个我们经常看到的词——32位/64位系统。

我们知道,计算机使用二进制数来存储信息,1位就是一个0或1的数字,32位系统指的是该系统的指针采用32位二进制数来表示。

指针的作用是指向一个虚拟内存地址(虚拟内存后面会讲,此处可当作内存来理解),也就是说,内存中的每一块都对应一个独有的指针的值。32位二进制可以表示的数的范围是0到2^32 - 1,也就是0到4294967296,最大值换算一下就是4千兆字节(4GB)。所以说,老的32位系统最大支持的内存就是4GB,再大的话,指针就无法表识,也就无法识别和使用超出4GB的内存区域了。

对于64位系统是同理的,64位系统的指针长度相当于32位指针长度的32次方,高达16EB!(1EB = 1024PB = 1024*1024TB = 1024*1024*1024GB


单位

接下来要讨论一个常见问题——单位。

1bit(1位)表示二进制数字的1位;1Byte(1字节)表示8位二进制数,它是计算机中表示、传输和操作数据的最小单位。

注意,1B=1Byte,1b=1bit,计算机中99%的情况下使用的都是Byte字节,bit位可以只把它当作一个数学定义。

日常生活中很多人认为运营商坑爹,百兆宽带下载速度才十几兆,其实就是通信领域和计算机领域中采用的数据单位不同而导致的误解。运营商宣称的网速单位是b/s,而我们实际在计算机中看到的网速单位是B/s。百兆宽带的100Mb/s除以8,也就是我们在计算机中看到的网速峰值12.5MB/s。

计算机中除字节外,另一个重要单位就是字长,字长其实在开头第一个问题就讲过了,指明的就是指针数据的标称大小。


整数

第三个问题是整数。

计算机中将整数分为无符号数和有符号数,用一个字长来表示,无符号数与有符号数的编码方式不同,导致数值范围、运算方式和溢出结果都极为不同。接下来的讲述用w来表识字长,w值为32或64。

无符号数

先说无符号数,也就是大于等于0的整数,无符号数的表示和运算很直观,与二进制对应: 第 i 位的权重就是2 ^ (i - 1),其范围是0 ~ (2^w - 1)

当无符号数不足w位需要拓宽时,在数值前面补0,大小不变;

当计算出的无符号数超过w位发生溢出时,其高位会被截断丢弃,结果会减去丢弃的位数与其权重的乘积。例如无符号数1011001,假设w=5,则结果被截断为11001。

有符号数

有符号数的表示和运算是本章的难点,但搞清楚原理就可以推导出各种结果了。有符号数采用“补码”的编码方式,与无符号数的区别是:

  • 其最高位的权重并非(2 ^ (w - 1)),而是复数(- 2 ^ (w - 1)),同时也作为符号位:最高位为1时表示负数,为0时表示正数。
  • 由上条可推出,有符号数的最小值为100…00,也就是最高位的权重(- 2 ^ (w - 1));最大值为011…11,即(2^(w-1) - 1)
  • 需要拓宽有符号数时:
    • 当最高位为0,则可以在高位直接补0拓宽,数值不变;
    • 当最高位为1时,补0会改变其符号,应该在高位补1,可以通过例子理解一下:有符号二进制数101换算成十进制就是-3,前面补1,1101、11101算下来则仍是-3。
  • 当有符号数发生溢出发生高位截断,但原理还是“减去”丢弃的位数与其权重的乘积,只不过当最高位为1时,权重为负,注意减去的是负值即可。

搞清楚原码和补码编码以后,就可以由此推导出整数的各种运算以及与有符号数与无符号数之间的转换了,本文不再赘述,读者可以自己推一推。


浮点数

最后一个问题是浮点数。

浮点数的表示可以分为三部分,总共32/64位:

  • 1位符号位s,s为1是表示负数,s为0表正数;
  • e位整数部分,在C语言中,float类型的e为8,double类型的e为11,表示的整数大小与二进制数值相同;
  • m位小数部分,C语言中占23位(float)或52位(double),表示的大小为“1.m”,比如m为01001,则表示1.01001。

最终表示的数值 = ((-1)^s) * (1.m) * (2^e)

这样的编码还有一些盲区:无法表示0~1之间的数;无法表示正负无穷和NaN。于是计算机规定了这些数值的表示方法:

  • 当00...00 < e < 11...11时,正常表示((-1)^s) * (1.m) * (2^e)
  • 当e = 00...00,即整数部分为0时,m的数值大小变为“0.m”,即((-1)^s) * (0.m)
  • 当e = 11...11,m = 00...00时,表示无穷,由符号位s来区分正无穷和负无穷;
  • 当e = 11...11,m不为0时表示NaN。

浮点数用于计算时采用“偶舍入”的方法,即“四舍六入,五取偶数”。

  • 如:1.4舍为1,1.6入为2,但1.5和2.5都会舍入为2,因为2为偶数。

“偶摄入”可避免统计偏差,因为原数末尾大于或小于一个偶数的概率相等,所以将原数变大或减小的概率是相等的。

理解了浮点运算的编码规则,就可以推出浮点运算的运算律:

  • 具有交换律
  • 不具有结合律,如 (3.14 + 1e10) - 1e10 会被舍入为0.0,而 3.14 + (1e10 - 1e10) 结果则为3.14
  • 浮点乘法不具有分配律
  • 浮点加法满足单调性:如果a > b,则(a+x) > (b+x),无符号和补码加法由于可能溢出舍入,不具有此单调性。