CSAPP 第二章 信息的表示和处理
Last updated on 2 years ago
信息的表示和处理
编码
无符号(Unsigned)数的编码
设一个整数数据有
对于向量
补码(Two's-complement)数的编码
对于向量
对于补码数的编码,其实就是无符号数的最高位变为负数,其余都是一样的
我们以
对于
对于
对于补码数而言,负数的个数有
其他编码方式
反码(Ones'Complement)编码
反码编码只是在补码的基础上将最高位的权重从
原码(Sign-Magnitude)编码
原码的最高位为符号位,其余位来决定该数的大小
这两种编码都有一个问题是,对于数字
在原码中
在反码中
实际上,浮点数的编码方式就是才有原码编码
无符号数与补码数的转换
对于补码和无符号数之间的转换,底层的位值表示是不变的,只是改变了解释这些位的方式
补码转无符号
无符号转补码
拓展一个数字的位表示
我们主要讨论从一个较小的类型转换到较大的类型
无符号的拓展
无符号的拓展被称为零拓展(zero extention),只需要简单的在开头添加
定义长度为
此时有:
补码数的拓展
补码数的拓展被称为符号拓展(sign extention),即在前面添加最高有效位
最高有效位为
定义长度为
我们有:
推导:
我们只需证明:
我们按照补码数的定义展开,有:
本质上是使用属性:
截断数字
截断无符号数
设
本质上是对于任意的
截断补码数
设
本质上是先将位向量截断为
整数运算
右移方式
右移方式有两种:逻辑右移与算术右移
前者会直接补
对于无符号数,右移均为逻辑右移
对于补码数,右移均为算术右移
无符号加法
设
两个
若两个
无符号加法溢出的检测
设
代码判断:
1 |
|
无符号的非
设
求无符号的非,本质上是对于一个无符号数
因此直接在
补码加法
设
对于
因此如果两个
如果结果大于
如果结果小于
补码加法溢出的判断
设
当且仅当
当且仅当
代码判断:
1 |
|
该程序不能写成:
1 |
|
这是因为,无论补码加法是否溢出,sum -x == y
都始终成立(补码加法是模数加法,具有循环的特性)
如果需要判断
1 |
|
当
当
当
正确写法:
1 |
|
补码的非
设
补码的非本质上同无符号的非一样,都是找一个数
在位表示上,对于任意值
快速求补码非的办法
在
这个值的非的位表示为
也就等同于,将
无符号乘法
对于
两个
补码乘法
对于
其实就是将乘法结果截断为
乘以常数
乘以 2 的次幂
我们先讨论乘以
无论是无符号数还是补码数,乘以
乘以任意常数
对于乘以任意常数
例如,
问题:如果加法、减法、移位所需的时间相同,那么当
分类讨论:
如果
如果
如果
综上,我们有如下结论:
- 若
,选第一种 - 若
,哪种都行 - 若
,选第二种
除以 2 的幂
取整的描述
对于除法,我们期望其结果总是舍去小数,这一点对正数和负数我们都期望是成立的
例如,我们期望:
这种舍入我们称为向零舍入,即直接舍去小数位
为了更便于描述该过程,我们需要引入两个符号:下取整与上取整
对于任意实数
例如:
我们发现,对于整数的向零舍入,恰好用下取整便可以描述,而对于负数的向零舍入,我们需要上取整才可以描述
无符号除法
若一个无符号数除以
例如:
对于无符号数值
补码除法
对于补码的除法,我们需要在右移的过程中保证符号不变,因此必须执行算术右移
我们从一个例子出发,考虑对负数直接执行算术右移的操作后,其结果如何
如果我期望将
我们发现,不论是逻辑右移还是算术右移,其结果都是下取整。这对于正数是合理的,但对于负数却不是我们希望的,我们需要一种办法将下取整转换为上取整
我们利用如下等式:
例如,当
对于补码数
到此为止,我们已经讨论了除以
你也许会想,任意一个数都可以分解为
这里的问题在于,除法会有舍入的问题(而乘法不具有这种问题),如果将其简单的分解为多个数相除,那么小数的部分将会全部被舍去,这将会导致最终的结果与我们所预期的相差过大
浮点数
表示
所有二进制小数均可以表示成以下形式:
数学定义如下:
基于此,我们给出
浮点数表示为:
- 符号 (
sign
) 用于决定该数为正数 还是负数- 单精度为
位,双精度为 位
- 单精度为
- 尾数 (
significand
) 是一个二进制小数,范围为 (denormal
) 或 (normal
)- 单精度为
位,双精度为
- 单精度为
- 阶码 (
exponent
) 用于对浮点数加权- 单精度为
位,双精度为
- 单精度为
下面我们以单精度为例,说明
Normal
当 exp
位表示不全为零(数值为零)也不全为一(单精度为
此时阶码值 exp
的无符号位表示,即
此时,单精度
此时 frac
字段的位表示用于描述小数值
Denormal
当 exp
的位表示全为零时为此种情况,此时阶码值
非规格数的作用是用于表示那些非常接近 gradual underflow
INF & NaN
当 exp
字段全为一且 frac
字段全为零时,表示无穷大,分为正无穷大与负无穷大
当 exp
字段全为一且 frac
字段不全为零时,表示 Not a Number
范围
Description |
exp |
frac |
float |
double |
---|---|---|---|---|
0 |
00...00 |
00...00 |
||
00...00 |
00...01 |
|||
00...00 |
11...11 |
|||
00...01 |
00...00 |
|||
01...11 |
00...00 |
|||
11...10 |
11...11 |
舍入
向上舍入
将正数与负数均向上舍入,得到值
正数 | 负数 |
---|---|
向下舍入
将正数与负数均向下舍入,得到值
正数 | 负数 |
---|---|
向零舍入
将正数向下舍入,负数向上舍入
正数 | 负数 |
---|---|
不难发现,相当于直接去掉小数位
向偶数舍入
将数字向上或向下舍入,使得最低有效位为偶数
对于十进制小数而言,小于
对于等于
具体来说,
对于二进制小数而言,小数点后所有小于
对于等于
具体来说,
上面的讨论均是以舍入到整数,如果是舍入到小数点后的情况,也是同理
浮点运算
我们定义浮点数加法
由于浮点数溢出和舍入,因此浮点数加法是可交换的但不可结合的,也就是只满足
例如,使用 float
计算
其次,除了
另一方面,浮点数满足单调性,即对于任意的
我们定义浮点数乘法
同样由于浮点数的舍入与溢出,乘法只具有交换性而不具有结合性,并且乘法也不具有分配性
例如,使用 float
计算
另一方面,浮点数的乘法满足单调性,即:
此外,如果保证
同样,无符号数与补码数并不具有这些特点
类型转换
- 当
float
或int
转double
时,由于double
具有更大的精度,因此不会发生溢出或舍入 - 当
int
转folat
时,会发生舍入但不会发生溢出 - 当
double
转folat
时,由于精度问题,会发生舍入与溢出(溢出到 ) - 当
float
或double
转int
时,值会发生向零舍入,即正数向下舍入负数向上舍入