我们在计算机系统中研究三种最重要的数字表示:
- 无符号(unsigned)编码基于传统的二进制表示法,表示大于等于零的数字。
- 补码编码是表示有符号整数最常见的方式,可以表示正数和负数。
- 浮点数编码是表示实数的科学计数法。
我们通过掌握计算机编码数值的二进制形式,在之后的学习中会更容易理解机器级代码。
我试着去重读一下CSAPP的第二章,但是结果显然是又臭又长,因此我对于第二章的复习完全基于袁春风教授的《计算机系统基础》,CSAPP对于某些简单概念的解释显然进行更为深刻的解读,但也为我们这些凡人的复习带来的不少麻烦,因此取长补短也是十分重要的。
2.1 数制与编码
2.1.1 信息的二进制编码
数据是计算机处理的对象,因此我们如何将不同的信息转换为不同的形式在计算机中存储这一问题,是我们之后研究整个计算机系统的基础,我们只有约定好如何定义各种数据的形式才会在后续的研究中消除其多义性。
我们下在计算机系统中的数据形式大概是按照下图这个表示形式:
显然在计算机中我们采用二进制的表现形式进行信息编码,以此为基础在计算机内部进行存储、运算和传送。
我们在转换中,也就是所谓的编码中的过程大概如下图所示:
我们在计算机中使用的机器指令也如图所示,无非是:无符号定点整数、带符号定点整数、浮点数和非数值型数据。处理的数据类型也是数值数据和非数值型数据(位串)。
因此,综上所述,我们表示一个数值数据要确定三个要素:进位计数制、定浮点表示和编码规则(原、补、反等)。
2.1.2 进位计数制
进位制(positional notation 或 place-value notation),是一种编码数的方式。在下面的说明中,默认十进制表示是个先验概念,如无特殊记号指明,所有数都是以十进制表示的。
我们在书写的时候使用后缀字母表示该数的进位计数制,一般用B表示二进制,用O表示八进制,用D表示十进制,用H表示十六进制,另外我们还使用0x作为十六进制的前缀。
进制转换
1.R进制转换成十进制
对于R进制转换成十进制,我们只需按上文位权展开即可:
(本系列主要为复习概念所用,会尽量减少举例说明)
2.十进制数转换成R进制数
任何一个十进制数转换成R进制数时,要将整数和小数部分分别进行转换:
(1)整数部分的转换:
整数部分转换方法“除基取余,上右下左”,这方法我说实话鬼能明白是啥意思,就是不断用权值对剩下的数取模,边除边取模。
(2)小数部分的转换:
小数部分的转换方法“乘基取整,上左下右”,要将转换的十进制小数去乘以权值R,将得到的乘积的整数部分作为结果中的数字,然后继续相乘。(这里要注意:可能小数部分总得不到零,我们只能得到一个近似值,我们取自己想要的位数就好了,这也说明我们现实中的精确值在机器内部有可能不能精确表示)
(这里还是放一道例题帮助理解)
3.二、八、十六进制数的相互转换
由于二、八、十六进制都是2的幂,因此转化起来较为简单;
(1)八进制转换为二进制
将每一位八进制数转换为相应的三位二进制数即可,同理二进制转换为八进制即从低位开始以三位为一组,每组转换为对应的八进制数即可。
(2)十六进制转换为二进制
将每一位十六进制数转换为相应的四位二进制数即可,同理二进制转换为十六进制即从低位开始以四位为一组,每组转换为对应的十六进制数即可。
2.1.3 定点与浮点表示
在计算机中,我们通过约定小数点的位置表示:
- 小数点位置约定在固定位置的数称为定点数;
- 小数点位置约定为可浮动的数称为浮点数。
定点表示
定点小数用来表示浮点数的尾数部分;
定点整数用来表示整数,包含带符号整数和无符号整数。
浮点表示
对于一个实数X,我们通常表示成:X = (-1)^S * M * R^E;
其中S用来决定符号;M是一个二进制定点小数,称为数X的尾数;E是一个二进制定点整数,称为数X的阶或指数;R是基数,可以为2、4、6等。
2.1.4 定点数的编码表示
原码表示法
一个数的原码表示由符号位直接跟数值位构成,浮点数的尾数使用原码的定点小数表示。
原码缺点:①0的表示不唯一;②原码加减运算规则复杂;③需单独处理符号位;④当a < b时实现a - b比较困难。
补码表示法
从五十年代开始,整数都采用补码来表示;
我们首先要理解一个概念:在一个模运算系统中,一个数与它除以模后的余数等价。
这个概念才是补码表示的重中之重,我们由此统一了 + 和 - ,因此我们做了鸵鸟,我们把头埋进沙子里面装作看不见溢出,这样在这个模系统构成的世界中进行我们计算机的运算。
补码的表示:整数的补码符号为0,数值部分是它本身;负数的补码等于模与该负数绝对值之差。
我们得出两个结论:
- 一个负数的补码等于模减该负数的绝对值;
- 对于某一确定的模,某数减去小于模的另一数,总可以用该数加上另一数负数的补码来代替。
TIPS:求负数补码更快一点的办法是各位取反末尾加一。
变形补码
为了便于判断运算结果是否溢出,我们使用双符号位的补码表示方式,在双符号位中,左符是真正的符号位,右符用于判断是否溢出。(01正溢、10负溢)
反码表示法
反码表示:
反码缺点:0的表示不唯一;表示的数字范围比补码小;运算时不许考虑循环进位。
主要用于数字检验以及中间表示(计网中就能看到反码的身影辣)
移码表示法
我们将每一个数值加上一个偏置常数,就构成了移码。
我们在浮点数中用阶码表示指数,因为阶码更方便我们去比较大小,浮点数进行加减运算要先对阶(即比较两个数的阶的大小并使之相等)。
2.2 整数的表示
计算机中的整数分为无符号整数和带符号整数两种。
2.2.1 无符号整数和带符号整数的表示
没啥要总结的,我也不知道她为啥分出来这节。
2.2.2 C语言中的整数及其相互转换
若同时有无符号数和带符号数,则C编译器将带符号数转换为无符号数。(就是只要算式中有无符号数就全按无符号数计算)
2.3 浮点数的表示
使用定点数表示数值范围会收到局限,无论是反码、补码、原码都是有范围的,因此我们专门使用浮点数来表示实数。
2.3.1 浮点数的表示范围
浮点数范围比定点数大,但数的个数没有变多,因此数之间更系数,且不均匀。
2.3.2 浮点数的规格化
这节也没啥说的。
2.3.3 IEEE 754 浮点数标准
32位单精度格式中包含一位符号位s,八位阶码e和23位尾数f;
64位双精度格式包含一位符号位s,11位阶码e和52位尾数f;
IEEE 754 规定隐藏位“1”的位置在小数点之前,阶码采用移码形式,偏置常数是(2^(n - 1) - 1),因此单精度和双精度浮点数的偏置常数分别为127和1023。
我们将IEEE 754中规定的数进行分类:
全0阶码全0尾数
表示+0/-0
全0阶码非0尾数:非规格化数
非规格化数的特点是阶码为全0,尾数高位有一个或几个连续的0,但不全为0,此时隐藏位为0,对于单精度和双精度的值分别为(-1)^s * 0.f * 2^(-126) 和 (-1)^s * 0.f * 2^(-1022)。
非规格化数用于处理阶码下溢,是得出现比最小规格化还小的数(最小规格化数2^(-126))时可以继续进行下去。
全1阶码全0尾数:+∞/-∞
当操作数为无穷大时,有两种处理方式:
- 产生不发信号的非数NaN;
- 产生明确的结果。
全1阶码非0尾数:NaN
NaN表示一个妹有定义的数,称为非数。对于静止非数,不发异常操作通知,即不进行异常处理;对于通知的NaN,需要发一个异常操作通知,要进行异常处理。
阶码非全0且非全1:规格化非0数
我才不会告诉你其实IEEE 754 看一张图就行了;
2.3.4 C语言中的浮点数类型
注意强制类型转换过程中导致的溢出或精度损失!!!
2.4 十进制数的表示
不想写了,注意一个各种BCD码就行。
2.5 非数值数据的编码表示
没啥想总结的,放张ASCⅡ码表吧。
2.6 数据的宽度和存储
2.6.1 数据的宽度和单位
这里只有一些需要注意的概念;
二进制的一位称为比特(bit),一个字节(Byte)包含八个比特。
字长指CPU内部用于整数运算的数据通路的宽度;字用来表示被处理信息的单位。
另:注意磁盘速度与带宽速度的单位区别。(这本应该是寄网应该提醒的事情吧)
2.6.2 数据的存储和排列顺序
在计算机存储时,多字节数据存放在连续地址中,按照排列顺序的不同分为大端存储方式和小端存储方式。
大端方式:将数据的最高有效字节存放在小地址单元中(先存取高位)
小端方式:将数据的最高有效字节存放在大地址单元中(先存取低位)
2.7 数据的基本运算
这一大节都没啥东西,记点容易忘的吧。
逻辑运算时非数值运算,结果只有两个逻辑值,按位运算是一种数值运算。
零标志OF
溢出标志OF
符号标志SF
进/借位标志CF
理论上来说CSAPP甚至不考加法器,那我后面的加法器和浮点运算都可以不看了哈哈。
后面就全摆烂了哇咔咔。