第二章 信息的表示和处理(page58 - page144)
本章我们研究在计算机上如何表示数字和其他形式数据的基本属性,以及计算机对这些数据执行操作的属性。这就要求我们深入研究数学语言,编写公示和方程式,以及展示重要属性的推导:
- 给出以数字形式表示的属性,作为原理
- 用例子和非形式化的讨论来解释这个原理
- 对于更复杂的属性,提供推导
2.1、信息存储
大多数计算机使用8位的快,或者字节,作为最小的可寻址的内存地址,而不是访问内存中单独的位。
程序将内存视为一个非常大的字节数组,称为虚拟内存,
内存的每个字节都由一个数字来标识,称为它的地址
所有可能地址的集合称为虚拟地址空间
C语言中指针的作用:
提供了引用数据结构的元素的机制。指针有两个方面:值和类型
值表示某个对象的位置,类型表示那个位置上所存储对象的类型
2.1.1、十六进制表示法
2.1.2、字数据大小
每台计算机都有一个字长,指明指针数据的标称大小。
对于一个字长为 w w w位的机器而言,虚拟地址的范围为0~ 2 w − 1 2^w-1 2w−1,程序最多访问 2 w 2^w 2w个字节
程序员应该力图使他们的程序在不同的机器和编译器上可移植。可移植性的一个方面就是使程序对不同数据类型的确切大小不敏感。C语言标准对不同数据类型的数字范围设置了下界,但是没有上界
2.1.3、寻址和字节顺序
对于跨越多字节的程序对象,我们必须建立两个规则:
- 这个对象的地址是什么
- 内存中如何排列这些字节
几乎在所有的机器上,多字节对象都被存储为连续的字节序列,对象的地址为所使用字节中最小的地址;排列表示一个对象字节中有两个通用的规则:
- 小端法:最低有效字节在最前面
- 大端法:最高有效字节在最前面
假设变量x的类型为int,位于地址0x100处,它的十六进制值为0x01234567。地址范围0x100 ~ 0x103的字节排列顺序为:
tips:
选择哪种字节顺序没有技术上的理由,小端(littlt endian)和大端(big endian)出自格列夫游记;其中交战的两个派别关于从一个半熟鸡蛋的哪一端打开的问题上无法达成一致。
对大多数程序员来说,其机器所使用的字节顺序是完全不可见的,无论是哪种类型的机器所编译的程序都会得到同样的结果。不过在部分情况下会有问题:
- 第一种情况:不过在两种机器之间传输数据时接受端程序会发现,字里的字节成了反序的。为了避免这个问题,网络应用程序的编写必须遵守已建立的关于字节顺序的规则;
- 第二种情况是,当阅读表示整数数据的字节序列时字节顺序也很重要。这通常发生在检查机器级程序时。
- 当编写规避正常的类型系统的程序时。在C语言中,可以通过强制类型转换或联合来允许以一种数据类型引用一个对象,而这种数据类型与创建这个对象时定义的数据类型不同。大多数应用编程都强烈不推荐这种编码技巧,但是对系统级编程来说非常有用,甚至是必须的
看看下面这段代码,它使用强制类型转换来访问和打印不同程序对象的字节表示:
#include <stdio.h>
typedef unsigned char *byte_pointer;
void show_bytes(byte_pointer start, size_t, len) {
size_t i;
for (i = 0; i < len; i++)
printf(" %.2x", start[i]);
printf("\n");
}
void show_int(int x) {
show_bytes((byte_pointer) &x, sizeof(int));
}
void show_float(float x) {
show_bytes((byte_pointer) &x, sizeof(float));
}
void show_float(void *x) {
show_bytes((byte_pointer) &x, sizeof(void *));
}
下图是在不同机器上的执行结果(备注: 12345 的十六进制表示为0x00003039):
2.1.4、表示字符串
C语言中的字符串被编码为一个以null(其值为0)字符结尾的字符数组。每个字符都由某个标准编码来表示,最常见的是ASCII字符码
2.1.5、表示代码:
int sum(int x, int y) {
return x + y;
}
2.1.6、布尔代数简介
扩展到向量上:
2.1.7、C语言中的位级运算
- 或操作:OR |
- 与操作: AND &
- 取反操作:NOT ~
- 异或操作:EXCLUSIVE-OR ^
确定一个位级表达式的结果最好的方法,就是将十六进制的参数扩展成二进制表示并执行二进制运算,最后再将结果转回十六进制
2.1.8、C语言中的逻辑运算
- 或操作:OR ||
- 与操作: AND &&
- 取反操作:NOT !
2.1.9、C语言中的移位运算
C语言提供了一组移位运算,向左或者向右移动位。
- x << k : x向左移动k位,丢弃最高的k位,并在右端补充k个0
- x >> k: x向右移动k位,
- 逻辑运算:在左端补充k个0(无符号数)
- 算数运算:在左端补充k个1 (有符号数)
注意:C语言中加减法的优先级比移位运算要高
2.2、整数表示
描述用位来编码整数的两种不同的方式:
- 只能表示负数
- 可以表示负数、零和正数
下图是整数的数据与算术操作术语:
2.2.1、整型数据类型
C语言支持多种整型数据类型(表示有限范围的整数):
注意,负数的范围比整数的范围大1
C语言整型数据类型的保证的取值范围:
2.2.2、无符号数的编码:
对向量 x ⃗ = [ x w − 1 , x w − 2 , . . . , x 0 ] \vec{x} = [x_{w-1}, x_{w-2}, ..., x_0] x=[xw−1,xw−2,...,x0]:
B 2 U w ( x ⃗ ) = . ∑ i = 0 w − 1 x i 2 i B2U_w(\vec{x}) \overset{\text{.}}{=} \sum_{i=0}^{w-1}x_i2^i B2Uw(x)=.i=0∑w−1xi2i
符 号 = . 表 示 左 边 被 定 义 等 于 右 边 。 符号\overset{\text{.}}{=}表示左边被定义等于右边。 符号=.表示左边被定义等于右边。
下面给出几个例子:
B 2 U 4 ( [ 0001 ] ) = 0 ∗ 2 3 + 0 ∗ 2 2 + 0 ∗ 2 1 + 1 ∗ 2 0 = 0 + 0 + 0 + 1 = 1 B2U_4([0001]) = 0*2^3 + 0*2^2 + 0*2^1 + 1*2^0 = 0+0+0+1 = 1 B2U4([0001])=0∗23+0∗22+0∗21+1∗20=0+0+0+1=1
B 2 U 4 ( [ 0101 ] ) = 0 ∗ 2 3 + 1 ∗ 2 2 + 0 ∗ 2 1 + 1 ∗ 2 0 = 0 + 4 + 0 + 1 = 5 B2U_4([0101]) = 0*2^3 + 1*2^2 + 0*2^1 + 1*2^0 = 0+4+0+1 = 5 B2U4([0101])=0∗23+1∗22+0∗21+1∗20=0+4+0+1=5
B 2 U 4 ( [ 1011 ] ) = 1 ∗ 2 3 + 0 ∗ 2 2 + 1 ∗ 2 1 + 1 ∗ 2 0 = 8 + 0 + 2 + 1 = 11 B2U_4([1011]) = 1*2^3 + 0*2^2 + 1*2^1 + 1*2^0 = 8+0+2+1 = 11 B2U4([1011])=1∗23+0∗22+1∗21+1∗20=8+0+2+1=11
B 2 U 4 ( [ 1111 ] ) = 1 ∗ 2 3 + 1 ∗ 2 2 + 1 ∗ 2 1 + 1 ∗ 2 0 = 8 + 4 + 2 + 1 = 15 B2U_4([1111]) = 1*2^3 + 1*2^2 + 1*2^1 + 1*2^0 = 8+4+2+1 = 15 B2U4([1111])=1∗23+1∗22+1∗21+1∗20=8+4+2+1=15
无符号数的二进制表示有个很重要的属性,每个介于 0 0 0 ~ ( 2 w − 1 ) (2^w-1) (2w−1)之间的数都有唯一一个w位的值编码;因此我们说 B 2 U w B2U_w B2Uw是一个双射。
2.2.3、补码编码
对向量 x ⃗ = [ x w − 1 , x w − 2 , . . . , x 0 ] \vec{x} = [x_{w-1}, x_{w-2}, ..., x_0] x=[xw−1,xw−2,...,x0]:
B 2 U w ( x ⃗ ) = . − x w − 1 2 w − 1 + ∑ i = 0 w − 1 x i 2 i B2U_w(\vec{x}) \overset{\text{.}}{=} -x_{w-1}2^{w-1}+\sum_{i=0}^{w-1}x_i2^i B2Uw(x)=.−xw−12w−1+i=0∑w−1xi2i
最高有效位 x w − 1 x_{w-1} xw−1也称为符号位,其权重为 − 2 w − 1 -2^{w-1} −2w−1
下面给出几个例子
B 2 T 4 ( [ 0001 ] ) = − 0 ∗ 2 3 + 0 ∗ 2 2 + 0 ∗ 2 1 + 1 ∗ 2 0 = 0 + 0 + 0 + 1 = 1 B2T_4([0001]) = -0*2^3 + 0*2^2 + 0*2^1 + 1*2^0 =\ 0+0+0+1 = 1 B2T4([0001])=−0∗23+0∗22+0∗21+1∗20= 0+0+0+1=1
B 2 T 4 ( [ 0101 ] ) = − 0 ∗ 2 3 + 1 ∗ 2 2 + 0 ∗ 2 1 + 1 ∗ 2 0 = 0 + 4 + 0 + 1 = 5 B2T_4([0101]) = -0*2^3 + 1*2^2 + 0*2^1 + 1*2^0 =\ 0+4+0+1 = 5 B2T4([0101])=−0∗23+1∗22+0∗21+1∗20= 0+4+0+1=5
B 2 T 4 ( [ 1011 ] ) = − 1 ∗ 2 3 + 0 ∗ 2 2 + 1 ∗ 2 1 + 1 ∗ 2 0 = − 8 + 0 + 2 + 1 = − 5 B2T_4([1011]) = -1*2^3 + 0*2^2 + 1*2^1 + 1*2^0 = -8+0+2+1 = -5 B2T4([1011])=−1∗23+0∗22+1∗21+1∗20=−8+0+2+1=−5
B 2 T 4 ( [ 1111 ] ) = − 1 ∗ 2 3 + 1 ∗ 2 2 + 1 ∗ 2 1 + 1 ∗ 2 0 = − 8 + 4 + 2 + 1 = − 1 B2T_4([1111]) = -1*2^3 + 1*2^2 + 1*2^1 + 1*2^0 = -8+4+2+1 = -1 B2T4([1111])=−1∗23+1∗22+1∗21+1∗20=−8+4+2+1=−1
补码所能表示的数据范围在:
−
2
w
−
1
至
2
w
−
1
−
1
之
间
-2^{w-1} 至 2^{w-1}-1之间
−2w−1至2w−1−1之间;
B
2
U
w
B2U_w
B2Uw也是一个双射。
另外,有符号数还有其他的表示方法:
2.2.4、有符号数和无符号数之间的转换
补码转换为无符号数:
对 满 足 T M i n w ≤ x ≤ T M a x w 的 x 有 : 对满足TMin_w \le x \le TMax_w的x有: 对满足TMinw≤x≤TMaxw的x有:
T 2 U w ( x ) = { x + 2 w , x < 0 x , x ≥ 0 T2U_w(x) = \left\{\begin{aligned}x+2^w, x < 0\\x, x \ge0\end{aligned}\right. T2Uw(x)={x+2w,x<0x,x≥0
无符号数转补码:
对 满 足 0 ≤ u ≤ U M a x w 的 u 有 : 对满足0 \le u \le UMax_w的u有: 对满足0≤u≤UMaxw的u有:
U 2 T w ( u ) = { u , u ≤ T M a x w u − 2 w , u > T M a x w U2T_w(u) = \left\{\begin{aligned}u, u \le TMax_w\\u-2^w,u > TMax_w\end{aligned}\right. U2Tw(u)={u,u≤TMaxwu−2w,u>TMaxw
2.2.5、C语言中的有符号数与无符号数
C语言支持所有整型数据类型的有符号和无符号运算
C语言允许无符号数和有符号数之间的转换
2.2.6、扩展一个数字的位表示
一个常见的运算是在不同字长的整数之间的转换,同时又保持数值不变。
-
无符号数的零扩展
要将一个无符号数转换为一个更大的数据类型,我们只要简单地在表示的开头添加0.
这种运算被称为零扩展,原理如下:
定 义 宽 度 为 w 的 位 向 量 u ⃗ = [ u w − 1 , u w − 2 , . . . , u 0 ] 和 宽 度 为 w ′ 的 位 向 量 u ′ ⃗ = [ 0 , . . . , 0 , u w − 1 , u w − 2 , . . . , u 0 ] , 其 中 w ′ > w 。 则 B 2 U w ( u ⃗ ) = B 2 U w ′ ( u ′ ⃗ ) 。 定义宽度为w的位向量\vec{u}=[u_{w-1},u_{w-2},...,u_0]和宽度为w'的位向量\vec{u'}=[0,...,0,u_{w-1},u_{w-2},...,u_0],其中w'> w。则B2U_w(\vec{u}) = B2U_{w'}({\vec{u'}})。 定义宽度为w的位向量u=[uw−1,uw−2,...,u0]和宽度为w′的位向量u′=[0,...,0,uw−1,uw−2,...,u0],其中w′>w。则B2Uw(u)=B2Uw′(u′)。
-
补码数的符号扩展
要将一个补码数字转换为一个更大的数据类型,可以执行一个符号扩展,在表示中添加最高有效位的值,表示为如下原理:
定 义 宽 度 为 w 的 位 向 量 x ⃗ = [ x w − 1 , x w − 2 , . . . , x 0 ] 和 宽 度 为 w ′ 的 位 向 量 x ′ ⃗ = [ 0 , . . . , 0 , x w − 1 , x w − 2 , . . . , x 0 ] , 其 中 w ′ > w 。 则 B 2 T w ( x ⃗ ) = B 2 T w ′ ( x ′ ⃗ ) 。 定义宽度为w的位向量\vec{x}=[x_{w-1},x_{w-2},...,x_0]和宽度为w'的位向量\vec{x'}=[0,...,0,x_{w-1},x_{w-2},...,x_0],其中w'> w。则B2T_w(\vec{x}) = B2T_{w'}({\vec{x'}})。 定义宽度为w的位向量x=[xw−1,xw−2,...,x0]和宽度为w′的位向量x′=[0,...,0,xw−1,xw−2,...,x0],其中w′>w。则B2Tw(x)=B2Tw′(x′)。
2.2.7、截断数字
假设我们不用额外的位来扩展一个数值,而是减少表示一个数字的位数。
-
截断无符号数
令 x ⃗ 等 于 向 量 [ x w − 1 , x w − 2 , . . . , x 0 ] , 而 x ′ ⃗ 是 将 其 截 断 为 k 位 的 结 果 : x ′ ⃗ = [ x k − 1 , x k − 2 , . . . , x 0 ] 。 令 x = B 2 U w x ⃗ , x ′ = B 2 U w x ′ ⃗ 。 则 x ′ = x m o d 2 k 。 令\vec{x}等于向量[x_{w-1},x_{w-2},...,x_0],而\vec{x'}是将其截断为k位的结果:\vec{x'}=[x_{k-1},x_{k-2},...,x_0]。令x=B2U_w{\vec{x}},x'=B2U_w{\vec{x'}}。则x'=x\ mod\ 2^k。 令x等于向量[xw−1,xw−2,...,x0],而x′是将其截断为k位的结果:x′=[xk−1,xk−2,...,x0]。令x=B2Uwx,x′=B2Uwx′。则x′=x mod 2k。
推导过程:
B 2 U w ( [ x w − 1 , x w − 2 , . . . , x 0 ] ) m o d 2 k = [ ∑ i = 0 w − 1 x i 2 i ] m o d 2 k = [ ∑ i = 0 k − 1 x i 2 i ] m o d 2 k = ∑ i = 0 k − 1 x i 2 i = B 2 U k ( [ x k − 1 , x k − 2 , . . . , x 0 ] ) B2U_w([x_{w-1},x_{w-2},...,x_0])\ mod\ 2^k = [\sum_{i=0}^{w-1}x_i2^i]\ mod\ 2^k \\ = [\sum_{i=0}^{k-1}x_i2^i]\ mod\ 2^k \\ =\sum_{i=0}^{k-1}x_i2^i \\ = B2U_k([x_{k-1},x_{k-2},...,x_0]) B2Uw([xw−1,xw−2,...,x0]) mod 2k=[i=0∑w−1xi2i] mod 2k=[i=0∑k−1xi2i] mod 2k=i=0∑k−1xi2i=B2Uk([xk−1,xk−2,...,x0])
在这段推导中,我们利用了属性: 对 于 任 何 i ≥ k , 2 i m o d 2 k = 0 对于任何i \ge k,2^i\ mod\ 2^k = 0 对于任何i≥k,2i mod 2k=0
-
截断补码数值
令 x ⃗ 等 于 向 量 [ x w − 1 , x w − 2 , . . . , x 0 ] , 而 x ′ ⃗ 是 将 其 截 断 为 k 位 的 结 果 : x ′ ⃗ = [ x k − 1 , x k − 2 , . . . , x 0 ] 。 令 x = B 2 U w x ⃗ , x ′ = B 2 T k x ′ ⃗ 。 则 x ′ = U 2 T k ( x m o d 2 k ) 。 令\vec{x}等于向量[x_{w-1},x_{w-2},...,x_0],而\vec{x'}是将其截断为k位的结果:\vec{x'}=[x_{k-1},x_{k-2},...,x_0]。令x=B2U_w{\vec{x}},x'=B2T_k{\vec{x'}}。则x'=U2T_k(x\ mod\ 2^k)。 令x等于向量[xw−1,xw−2,...,x0],而x′是将其截断为k位的结果:x′=[xk−1,xk−2,...,x0]。令x=B2Uwx,x′=B2Tkx′。则x′=U2Tk(x mod 2k)。
2.2.8、关于有符号数和无符号数的建议
有符号数到无符号数的隐式强制类型转换导致了某些非直观的行为。从而导致程序错误,且很难被发现;看一个例子:
2.3、整数运算
理解计算机运算的细微之处能够帮助程序员编写更可靠的代码
2.3.1、无符号加法
-
无符号加法
对 满 足 0 ≤ x , y < 2 w 的 x 和 y 有 : 对满足0\le x,y<2^w的x和y有: 对满足0≤x,y<2w的x和y有:
x + w u y = { x + y , x + y < 2 w 正 常 x + y − 2 w , 2 w ≤ x + y < 2 w + 1 溢 出 x+^u_wy= \left\{\begin{aligned} x+y,x+y<2^w\ 正常\\ x+y-2^w,2^w\le x+y<2^{w+1}\ 溢出 \end{aligned}\right. x+wuy={x+y,x+y<2w 正常x+y−2w,2w≤x+y<2w+1 溢出
检查无符号数加法中的溢出:
对 在 范 围 0 ≤ x , y ≤ U M a x w 中 的 x 和 y , 令 s = . x + w u y 。 则 对 计 算 s , 当 且 仅 当 s < x ( 或 者 等 价 地 s < y ) 时 , 发 生 了 溢 出 对在范围0\le x,y\le UMax_w中的x和y,令s\overset{\text{.}}=x+^u_wy。则对计算s,当且仅当s<x(或者等价地s<y)时,发生了溢出 对在范围0≤x,y≤UMaxw中的x和y,令s=.x+wuy。则对计算s,当且仅当s<x(或者等价地s<y)时,发生了溢出
-
无符号数求反
对 满 足 0 ≤ x < 2 的 任 意 x , 其 w 位 的 无 符 号 逆 元 − w u x 由 下 式 给 出 : 对满足0\le x< 2的任意x,其w位的无符号逆元-^u_wx由下式给出: 对满足0≤x<2的任意x,其w位的无符号逆元−wux由下式给出:
− w u x = { x , x = 0 2 w − x , x > 0 -^u_wx= \left\{\begin{aligned} x,x=0 \\ 2^w-x,x>0 \end{aligned}\right. −wux={x,x=02w−x,x>0
2.3.2、补码加法
对 满 足 − 2 w − 1 ≤ x , y ≤ 2 w − 1 − 1 的 整 数 x 和 y , 有 : 对满足-2^{w-1} \le x, y\le 2^{w-1}-1的整数x和y,有: 对满足−2w−1≤x,y≤2w−1−1的整数x和y,有:
x
+
w
u
y
{
x
+
y
−
2
w
,
2
w
−
1
≤
x
+
y
正
溢
出
x
+
y
,
−
2
w
−
1
≤
x
+
y
<
2
w
−
1
正
常
x
+
y
+
2
w
,
x
+
y
<
−
2
w
−
1
负
溢
出
x+^u_wy \left\{\begin{aligned} x+y-2^w, 2^{w-1}\le x+y \ 正溢出\\ x+y,-2^{w-1}\le x+y < 2^{w-1}\ 正常\\ x+y+2^w,x+y<-2^{w-1}\ 负溢出 \end{aligned}\right.
x+wuy⎩⎪⎨⎪⎧x+y−2w,2w−1≤x+y 正溢出x+y,−2w−1≤x+y<2w−1 正常x+y+2w,x+y<−2w−1 负溢出
2.3.3、补码的非
对 满 足 T M i n w ≤ x ≤ T M a x w 的 x , 其 补 码 的 非 − w t x 由 下 式 给 出 : 对满足TMin_w \le x \le TMax_w的x,其补码的非-^t_wx由下式给出: 对满足TMinw≤x≤TMaxw的x,其补码的非−wtx由下式给出:
− w t x = { T M i n w , x = T m i n w − x , x > T M i n w -^t_wx = \left\{\begin{aligned} TMin_w,x=Tmin_w \\ -x ,\ x> TMin_w \end{aligned}\right. −wtx={TMinw,x=Tminw−x, x>TMinw
2.3.4、无符号乘法
对 满 足 0 ≤ x , y ≤ U M a x w 的 x 和 y 有 : 对满足0 \le x, y \le UMax_w的x和y有: 对满足0≤x,y≤UMaxw的x和y有:
x ∗ w u y = ( x ⋅ y ) m o d 2 w x * ^u_wy = (x \cdot y )\ mod \ 2^w x∗wuy=(x⋅y) mod 2w
2.3.5、补码乘法
-
补码乘法:
对 满 足 T M i n w ≤ x , y ≤ T M a x w 的 x 和 y 有 : 对满足TMin_w \le x, y \le TMax_w的x和y有: 对满足TMinw≤x,y≤TMaxw的x和y有:
x ∗ w t y = U 2 T w ( ( x ⋅ y ) m o d 2 w ) x * ^t_wy =U2T_w((x \cdot y)\ mod \ 2^w) x∗wty=U2Tw((x⋅y) mod 2w)
-
无符号和补码乘法的位级别等价性
给 定 长 度 为 w 的 位 向 量 x ⃗ 和 y ⃗ , 用 补 码 形 式 的 位 向 量 表 示 来 定 义 整 数 x 和 y : x = B 2 T w ( x ⃗ ) , y = B 2 T w ( y ⃗ ) 。 用 无 符 号 形 式 的 位 向 量 表 示 来 定 义 非 负 整 数 x ′ 和 y ′ : x ′ = B 2 U w ( x ⃗ ) , y ′ = B 2 U w ( y ′ ⃗ ) 。 则 给定长度为w的位向量\vec{x}和\vec{y}, 用补码形式的位向量表示来定义整数x和y:x=B2T_w(\vec{x}),y=B2T_w(\vec{y})。用无符号形式的位向量表示来定义非负整数x'和y':x'=B2U_w(\vec{x}),y'=B2U_w(\vec{y'})。则 给定长度为w的位向量x和y,用补码形式的位向量表示来定义整数x和y:x=B2Tw(x),y=B2Tw(y)。用无符号形式的位向量表示来定义非负整数x′和y′:x′=B2Uw(x),y′=B2Uw(y′)。则
T 2 B w ( x ∗ w t y ) = U 2 B w ( x ′ ∗ w u y ′ ) T2B_w(x*^t_wy) = U2B_w(x'*^u_wy') T2Bw(x∗wty)=U2Bw(x′∗wuy′)
可以看个乘法溢出的例子:
2.3.6、乘以常数
-
乘以2的幂
设 x 为 位 模 式 [ x w − 1 , x w − 2 , . . . , x 0 ] 表 示 的 无 符 号 整 数 , 那 么 , 对 于 任 何 k ≥ 0 , 我 们 都 认 为 [ [ x w − 1 , x w − 2 , . . . , x 0 , 0 , . . . , 0 ] 给 出 了 x 2 k 的 w + k 位 的 无 符 号 表 示 , 这 里 右 边 增 加 了 k 个 0. 设x为位模式[x_{w-1},x_{w-2},...,x_0]表示的无符号整数,那么,对于任何k \ge 0,我们都认为[[x_{w-1},x_{w-2},...,x_0,0,...,0]给出了x2^k的w+k位的无符号表示,这里右边增加了k个0. 设x为位模式[xw−1,xw−2,...,x0]表示的无符号整数,那么,对于任何k≥0,我们都认为[[xw−1,xw−2,...,x0,0,...,0]给出了x2k的w+k位的无符号表示,这里右边增加了k个0.
-
与2的幂相乘的无符号乘法
C 变 量 x 和 k 有 无 符 号 数 值 x 和 k , 且 0 ≤ k < w , 则 C 表 达 式 x < < k 产 生 数 值 x ∗ w u 2 k 。 C变量x和k有无符号数值x和k,且0 \le k< w,则C表达式\\ x<<k产生\ 数值 \ x*^u_w2^k。 C变量x和k有无符号数值x和k,且0≤k<w,则C表达式x<<k产生 数值 x∗wu2k。
2.3.7、除以2的幂
-
除以2的幂的无符号除法
C 变 量 x 和 k 有 无 符 号 数 值 x 和 k , 且 0 ≤ k < w , 则 C 表 达 式 x > > k 产 生 数 值 [ x / 2 k ] 。 C变量x和k有无符号数值x和k,且0 \le k< w,则C\\ 表达式x>>k产生\ 数值 \ [x/2^k]。 C变量x和k有无符号数值x和k,且0≤k<w,则C表达式x>>k产生 数值 [x/2k]。
-
除以2的幂的补码除法,向下舍入
C 变 量 x 和 k 分 别 有 补 码 值 x 和 无 符 号 数 值 k , 且 0 ≤ k < w , 则 当 执 行 算 术 移 位 时 , C 表 达 式 x > > k 产 生 数 值 [ x / 2 k ] 。 C变量x和k分别有补码值x和无符号数值k,且0 \le k< w,则当执行算术移位时,C表达式x>>k产生\ 数值 \ [x/2^k]。 C变量x和k分别有补码值x和无符号数值k,且0≤k<w,则当执行算术移位时,C表达式x>>k产生 数值 [x/2k]。
-
除以2的幂的补码除法,向上舍入
C 变 量 x 和 k 分 别 有 补 码 值 x 和 无 符 号 数 值 k , 且 0 ≤ k < w , 则 当 执 行 算 术 移 位 时 , C 表 达 式 ( x + ( 1 < < k ) − 1 ) > > k 产 生 数 值 [ x / 2 k ] 。 C变量x和k分别有补码值x和无符号数值k,且0 \le k< w,则当执行算术移位时,C表达式(x+(1<<k)-1)>>k产生\ 数值 \ [x/2^k]。 C变量x和k分别有补码值x和无符号数值k,且0≤k<w,则当执行算术移位时,C表达式(x+(1<<k)−1)>>k产生 数值 [x/2k]。
2.3.8、关于整数运算的思考
计算机执行的“整数”运算实际上是一种模运算形式。表示数字的有限字长限制了可能的值的取值范围,结果运算可能溢出。补码表示提供了一种既能表示负数也能表示整数的灵活方法,同时使用了与执行无符号算术相同的位级实现,这些运算包括加法、减法、乘法甚至除法,无论运算数是以无符号形式还是以补码形式表示的,都有完全一样或者非常类似的位级行为
2.4、浮点数
有理数表示对形如 V = x × 2 y V=x\times 2^y V=x×2y的有理数进行编码。它对执行涉及非常大的数字 ( ∣ V ∣ > > 0 ) (|V|>> 0) (∣V∣>>0)、非常接近于 0 ( ∣ V ∣ < < 1 ) 0(|V| << 1) 0(∣V∣<<1)的数字,以及更普遍地作为实数运算的近似值的计算,是很有用的。
在本节中,我们将看到IEEE浮点格式中数字是如何表示的,并讨论舍入(rounding)的问题
2.4.1、二进制小数
首先看下我们熟悉的十进制表示法:
d m d m − 1 ⋅ ⋅ ⋅ d 1 d − 1 d − 2 ⋅ ⋅ ⋅ d − n d_md_{m-1} \cdot\cdot\cdot d_1d_{-1}d_{-2}\cdot\cdot\cdot d_{-n} dmdm−1⋅⋅⋅d1d−1d−2⋅⋅⋅d−n
其中每个十进制数 d i d_i di的取值范围是0 ~ 9 。这个表达描述的数值d定义如下:
d = ∑ i = − n m 1 0 i × d i d = \sum_{i=-n}^m10^i \times d_i d=i=−n∑m10i×di
浮点数中,小数点左边的数字的权为10的正幂,右边的数字的权为10的负幂,例如:
12.3 4 10 = 1 × 1 0 1 + 2 × 1 0 0 + 3 × 1 0 − 1 + 4 × 1 0 − 2 = 12 34 100 12.34_{10} = 1 \times 10^1 + 2 \times 10 ^0 + 3 \times 10 ^{-1} + 4 \times 10^{-2} = 12 \frac{34}{100} 12.3410=1×101+2×100+3×10−1+4×10−2=1210034
类似地对形如 b m b m − 1 ⋅ ⋅ ⋅ b 1 b − 1 b − 2 ⋅ ⋅ ⋅ b − n b_mb_{m-1} \cdot\cdot\cdot b_1b_{-1}b_{-2}\cdot\cdot\cdot b_{-n} bmbm−1⋅⋅⋅b1b−1b−2⋅⋅⋅b−n 的二进制:
b = ∑ i = − n m 2 i × b i b = \sum_{i=-n}^m2^i \times b_i b=i=−n∑m2i×bi
2.4.2、IEEE浮点表示
IEEE浮点标准用 V = ( − 1 ) s × M × 2 E V = (-1)^s \times M \times 2^E V=(−1)s×M×2E的形式来表示一个数:
- 符号:s决定这个数是负数(s=1)还是正数(s=0)
- 尾数:M是一个二进制小数,它的范围是1 ~ 2 - ϵ \epsilon ϵ,或者是0~1 - ϵ \epsilon ϵ 。
- 阶码: E的作用是对浮点数加权,这个权重是2的E次幂(可能是负数)
将浮点数的位表示划分为三个字段
- 一个单独的符号位s直接编码符号s
- k k k位的阶码字段 e x p = e k − 1 ⋅ ⋅ ⋅ e 1 e 0 exp = e_{k-1} \cdot\cdot\cdot e_1e_0 exp=ek−1⋅⋅⋅e1e0编码阶码E。
- n位小数字段 f r a c = f n − 1 ⋅ ⋅ ⋅ f 1 f 0 frac = f_{n-1} \cdot\cdot\cdot f_1f_0 frac=fn−1⋅⋅⋅f1f0编码尾数M,但是编码出来的值也依赖于阶码字段的值是否等于0
2.4.3、数字示例
2.4.4、舍入
2.4.5、浮点运算
IEEE标准指定了一个简单的规则,来确定诸如加法和乘法这样的算术运算的结果。
把浮点数x和y看成实数,而某个运算 ⊙ \odot ⊙定义在实数上,计算将产生 R o u n d ( x ⊙ y ) Round(x\odot y) Round(x⊙y) ,这是对实际运算的精确结果进行舍入后的结果。
当参数中有一个是特殊值(如-0, − ∞ -\infty −∞或NaN)时,IEEE定义了一些使之更合理的规则,如:
1 / − 0 将 产 生 − ∞ ; 1 / + 0 将 产 生 + ∞ 1/-0 将产生 -\infty;1/+0 将产生 +\infty 1/−0将产生−∞;1/+0将产生+∞
2.4.6、C语言中的浮点数
当在int、float和double格式之间进行强制类型转换时,程序改变数值和位模式的原则如下(假设int是32位的):
- 从int转换成float,数字不会溢出,但可能被舍入
- 从int或float转换成double,因为double有更大的范围(也就是可表示值的范围),也有更高的精度(也就是有效位数),所以能够保留精确的数值。
- 从double转换成float,因为范围要小一些,所以值可能溢出成 + ∞ 或 − ∞ 。 +\infty或-\infty。 +∞或−∞。另外,由于精度较小,它可能被舍入
- 从float或double转换成int,值将会向零舍入。例如1.9999将会转换为1,而-1.999将会转换为-1;进一步来说可能会溢出
下面是一个浮点溢出的例子: