CSAPP阅读笔记-第2章 信息的表示和处理
CSAPP阅读笔记
原码、反码与补码的含义
为运算方便,机器数有 3 种表示法,即原码、反码和补码。
原码
原码是一种计算机中对数字的二进制定点表示法。原码表示法在数值前面增加了一位符号位(即最高位为符号位):正数该位为 0,负数该位为 1(0 有两种表示:+0 和 -0),其余位表示数值的大小。举个例子,我们用 8 位二进制表示一个数,+12 的原码为 00001100,-12 的原码就是 10001100。
反码
当数字用原码进行运算时,因为需要先判断数字的正负,这样导致效率不高。若忽略符号位直接进行运算,则需要用到反码,但是直接使用反码也会出现问题。
正数的反码和原码一样,负数的反码就是在原码的基础上符号位保持不变,其他位取反。
十进制 | 原码 | 反码 |
---|---|---|
4 | 0000 0100 | 0000 0100 |
-2 | 1000 0010 | 1111 1101 |
若进行运算4-2,即4+(-2)
0000 0100(反码)
1111 1101(反码)
--------------
0000 0001(反码)
0000 0001(原码)
发现在使用反码进行4-2运算后最后得到的结果为1。为解决在运算中出现不准确的问题,则出现了补码运算。
补码
正数和 0 的补码就是该数字本身。负数的补码则是将其对应正数按位取反再加 1。补码系统的最大优点是可以在加法或减法处理中,不需因为数字的正负而使用不同的计算方式。
补码的思想:类似于一个钟现在是十点,如果要把它调到八点,可以往前调两个小时,也可以往后调十个小时
6 + (-3)
以补码形式的计算过程如下:
6 - 3 ==> 6 + (-3)
0000 0110 // 6(补码)
+ 1111 1101 // -3(补码)
----------------------
0000 0011 // 3(补码)
第二章
三种重要的数字表示:
- 无符号编码基于传统的二进制表示法,表示大于或者等于零的数字。
- 补码编码是表示有符号整数最常见的方法,有符号整数即可以为正或者为负的数字。
- 浮点数编码是表示实数的科学计数法的以2为基数的版本。
整数的表示虽然只能编码一个相对较小的数值范围,但它是精确的;而浮点数虽然可以编码一个较大的范围,但这种表示是近似的。
2.1 信息存储
大多数计算机使用8位的块,或者字节(byte),作为最小的可寻址的内存单位,而不是访问内存中单独的位。机器级程序将内存视为一个非常大的字节数组,成为虚拟内存。内存的每个字节都由一个唯一的数字来标识,成为它的地址,所有可能地址的集合就成为虚拟地址空间。
2.1.1 十六进制表示法
一个字节由八位组成。在二进制表示法中,它的值域是000000002~111111112。在C语言中,以0x或者0X开头的数字常量被认为是十六进制的值。
2.1.2 字数据大小
每台计算机都有一个字长,指明指针数据的标称大小。字长决定的最重要的系统参数就是虚拟地址空间的最大大小。对于一个字长为w位的机器而言,虚拟地址的范围为\(0~2^w -1\),程序最多访问\(2^w\)个字节。
为避免由于依赖“典型”大小和不同编译器设置而带来的奇怪行为,ISO C99引入了一类数据类型,其数据大小是固定的,不随编译器和机器设置而变化。其中就有数据类型int32_t
和int64_t
,它们分别为4个字节和8个字节。
2.1.3 寻址与字节顺序
在几乎所有的机器上,多字节对象都被存储为连续的字节序列,对象的地址为所使用字节中最小的地址。例如,一个类型为int的变量x的地址为0x100,即是说&x的值为0x100。那么,(假设数据类型int为32位表示)x的4个字节将被存储在内存的0x100、0x101、0x102和0x103的位置。
int x[3] = {1,2,3};
for(int i = 0;i<3;i++){
std::cout<<(x+i)<<std::endl;
}
输出结果:
0x30e11966c
0x30e119670
0x30e119674
若有一个w位的整数,其位表示位[xw-1,xw-2,...,x1,x0],其中xw-1为最高有效位,x0为最低有效位。假设w为8的倍数,则这些位都能被分为字节,其中最高有效字节包括位[xw-1,xw-2,...,xw-8],最低有效位字节包括位[x7,x6,...,x0]。有两种规则,最低有效位在最前面的方法称为小端法,最高有效位在最前面的方法称为大端法。
假设int类型变量x位于地址0x100处,它的十六进制值为0x01234567(高位字节的十六进制值为0x01,低位字节值为0x67)。地址范围0x100~0x103的字节顺序依赖于机器的类型:
大多数Intel兼容机都只用小端模式。IBM和Oracl的大多数机器则按大端模式操作。
使用typedef来命名数据类型
typedef的语法与声明变量的语法十分相像,除了它使用的是类型名而不是变量名。
例如:
typedef int *int_pointer; int_pointer ip;
将类型"int_pointer"定义为一个指向int的指针,并且声明了一个这种类型的变量ip。等同于:
int *ip
2.1.4 表示字符串
C语言中字符串被编码为一个以null(值为0)字符结尾的字符数组。每个字符都由某个标准编码来表示,最常见的是ASCII字符码。在使用ASCII码作为字符码的任何系统上都将得到相同的结果,与字节顺序和字大小规则无关。因而,文本数据比二进制数据具有更强的平台独立性。
2.1.5 表示代码
以下的C函数:
int sum(int x, int y){
return x + y;
}
在不同的机器上编译时,生成了如下的机器代码:
Linux 32 64 55 89 e5 8b 45 Oc 03 45 08 c9 c3
Windows 55 89 e5 8b 45 0c 03 45 08 5d c3
Sun 81 c3 eO 08 90 02 00 09
Linux 55 48 89 e5 89 7d f c 89 75 f 8 03 45 f c c9 c3
2.1.6 布尔代数简介
布尔代数运算
位向量一个很有用的应用就是表示有限集合。可以用位向量[aw-1,···,a1,a0]编码任何子集A\(\subseteq\){0,1,···,w-1},其中ai=1当且仅当i\(\in\)A。
使用这种编码集合的方法,布尔运算|和&对应于集合的并和交,而~对应于集合的补。
2.1.7 C语言中的位级运算
C语言的特性之一就是它支持按位布尔运算。|就是OR(或),&就是AND(与),~就是NOT(取反),而^就是EXCLUSIVE- OR(异或)。
2.1.8 C语言中的逻辑运算
2.1.9 C语言中的移位运算
C语言提供了一组移位运算,向左或向右移动位模式。x<<k即x向左移动k位,丢弃最高的k位,并在右端补k个0。
对应的右移运算x>>k,机器支持两种形式的右移:逻辑右移和算数右移。逻辑右移在左端补k个0,算术右移是在左端补k个最高有效位的值。
实际上,几乎所有的编译器/机器护额都对有符号数进行算数右移,且许多程序员也都假设机器会使用这种右移。另一方面,对于无符号数,右移必须是逻辑的。
2.2 整数表示
下图列出了一些引入的数学术语,用于精确定义与描述计算机如何编码和操作整数。
各种编码的表示
对向量\(\vec x\)=[xw-1,xw-2,···,x0]:
1.无符号编码
B2Uw(\(\vec x\))\(\doteq\)\(\sum_{i=0}^{w-1}\)xi2\(^i\)
符号\(\doteq\)表示左边被定义为等于右边
2.补码编码
B2Tw(\(\vec x\))\(\doteq\)\(-\)xw-12w-1\(+\)\(\sum_{i=0}^{w-2}\)xi2i
最高有效位xw-1也称为符号为,它的“权重”为$-$2w-1
可以注意到补码的范围是不对称的:|TMin|\(=\)|Tmax|+1。
有符号数的其他表示方法
反码:除了最高有效位的权是\(-\)(2w-1$-\(1)而不是\)-$2w-1,其他与反码相同
B2Ow(\(\vec x\))\(\doteq\)\(-\)xw-1(2w-1$-\(1)+\)\sum_{i=0}{w-2}$x~i~2i^
原码:最高有效位是符号位
B2Sw(\(\vec x\))\(\doteq\) \(({-}1)^{x_{w-1}}\)\(*\)($\sum_{i=0}{w-2}x_{i}$2i^)
3.补码转换为无符号数
![image-20220908185639867](/Users/criskey/Library/Application Support/typora-user-images/image-20220908185639867.png)
当w=4时,补码表示的负数如果看为无符号数,-5会变成11。
4.无符号数转换为补码
![image-20220908190905856](/Users/criskey/Library/Application Support/typora-user-images/image-20220908190905856.png)
对于-2147483647-1转换成无符号数为2147483648>2147483647
2.2.1 扩展与截断一个数字位的表示
将一个无符号数转换为一个更大的数据类型,只需要简单地在表示的开头添加0.这种运算被称为0扩展。
无符号数的符号扩展:
补码数的符号扩展:
截断无符号数:
截断补码数值:
2.3 整数表示
2.3.1 无符号加法
为参数x与参数y定义运算\(+^{u}_{w}\),其中0\(\leqslant\)x,y$<\(2^w^,该操作是把整数和x\)+$y截断为w位得到的结果,再把这个结果看成一个无符号数。
无符号数加法
检测无符号数加法的溢出
无符号数求反
对于每个值\(x\),必然有某个值\(-^{u}_{w}x\)满足\(-^{u}_{w}x\)\(+^{u}_{w}x=0\)。该加法的逆操作可以表示为:
2.3.2 补码加法
定义\(x+^{t}_{w}y\)为整数和\(x+y\)被截断位\(w\)位的结果,并将这个结果看做是补码数。
检测补码加法中的溢出
2.3.4 无符号乘法
2.3.5 补码乘法
2.4 浮点数
IEEE浮点数表示
IEEE浮点标准用\(V=(-1)^s\cdot M\cdot2^E\)的形式来表示一个数:
将浮点数划分为了三段
- 符号(sign):s决定了数为正数\((s=1)\)还是负数\((s=0)\)
- 尾数(significand): M是一个二进制小数,范围为\([1.0,2.0)\)。n位小数字段\(frac=f_n-1 ···f_1 f_0\)编码尾数M
- 阶码(exponent):E的作用是对浮点数加权,这个权重是2的E次幂(可能是负数)。k位的阶码字段\(exp=e_k-1···e_1e_0\)
给定位表示,根据exp的值,被编码的值可以分为三种不同的情况。
1.规格化的值
exp不为全0或全1,阶码的值\(E=e-Bias\),其中\(e\)为无符号数,表示为\(e_k-1···e_1e_0\),而\(Bias\)是一个等于\(2^{k-1}\)的偏置值(单精度为127,双精度为1023)。由此产生的指数的取值范围,对于单精度为\(-126\)~\(+127\),而对于双精度是\(-1022\) ~ \(+1023\) 。尾数定义为\(M=1+f\)
2.非规格化
阶码值是\(E=1-Bias\) ,而尾数的值是\(M=f\).
非规格化提供了一种表示数值0的方法,同时也提供了另一个功能就是表示那些非常接近于0.0的数,这种属性叫逐渐下溢。
3.特殊值
当阶码全为1时
- 若小数域全为0,得到的值表示无穷,当\(s=0\)时是\(+∞\),\(s=1\)时为\(-∞\)。当把两个非常大的数相乘或者除以零时,无穷可以表示溢出。
- 若小数域为非零,结果值“NaN”即"Not a Number",若运算的结果不能是实数或无穷时就会得到这样的结果。
实例:
舍入
向偶数舍入(round-to-even),也被称为向最接近的值舍入,是默认的方式。
浮点数运算
浮点数运算可交换不可结合。
本文来自博客园,作者:我只有一天的回忆,转载请注明原文链接:https://www.cnblogs.com/cc-coding/articles/16658884.html