深入理解计算机系统 2.1 节信息存储,深入理解计算机系统(原书第3版)- 第2章 信息的表示和处理 笔记...

2.1 信息存储

1字节(byte)= 8位(bit)

2.1.1 十六进制表示法

十六进制(简写为"hex")使用数字 '0' ~ '9' 以及字符 'A' ~ 'F' 来表示。

1 个十六进制数表示 4 个二进制数。

当 x=2n 时,很容易将 x 写成十六进制形式,x 的二进制表示就是 1 后面跟 n 个 0 。十六进制数字 0 代表 4 个二进制 0。所以,当 n 表示成 i+4j 的形式,其中 0 ≦ i ≦ 3,x 开头十六进制为1(i=0)、2(i=1)、4(i=2)、8(i=3),后面跟随 j 个十六进制的 0。

2.1.2 字数据大小

C声明

C声明

字节数

字节数

有符号

无符号

32位

64位

[signed] char

unsigned char

1

1

short

unsigned short

2

2

int

unsigned

4

4

long

unsigned long

4

8

int32_t

uint32_t

4

4

int64_t

uint64_t

8

8

char *

4

8

float

4

4

double

8

8

2.1.3 寻址和字节顺序

最低有效字节在最前面的方式,为小端法(little endian)。

最高有效字节在最前面的方式,为大端法(big endian)——与书写习惯一致。

假设变量 x 的类型为 int,位于地址 0x100 处,它的十六进制值为 0x01234567。地址范围 0x100〜0x103 的字节顺序依赖于机器的类型:

注意,在字 0x01234567 中,高位字节的十六进制值为 0x01,而低位字节值为 0x67。

ab3a675f908e?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

// 判断大小端

int is_little_endian(void) {

int i = 0xff;

unsigned char *p = &i;

return p[0] == 0xff;

}

2.1.4 表示字符串

C语言中字符串被编码为一个以null(其值为0)字符结尾的字符数组。

字母 'a' ~ 'z' 的 ASCII 码为 0x61 ~ 0x7A。

2.1.5 表示代码

不同的机器类型使用不同的且不兼容的指令和编码方式。

2.1.6 布尔代数简介

1 为 true(真)

0 为 false(假)

图2-7↓ 布尔代数的运算。二进制值 1 和 0 表示逻辑值 TRUE 或者 FALSE ,而运算符 〜、&、丨和 ^ 分别表示逻辑运算 NOT、 AND、OR 和 EXCLUSIVE-OR

ab3a675f908e?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

可以将上述 4 个布尔运算扩展到位向量的运算,位向量就是固定长度为 W 、由 0 和 1 组成的串。位向量的运算可以定义成参数的每个对应元素之间的运算。

举个例子,假设 w=4 ,参数 a = [0110] ,b=[1100]。那么 4 种运算a&b、a|b、a^b 和〜b 分别得到以下结果:

ab3a675f908e?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

关于布尔代数和布尔环的更多内容

布尔运算 & 对 | 的分配律,写为 a & (b | c) = (a & b) | (a & c)。

布尔运算 | 对 & 的分配律,写为 a | (b & c) = (a | b) & (a | c)。

对于任何值 a 来说,a ^ a = 0。

(a ^ b) ^ a = b。

2.1.7 C语言中的位级运算

C的表达式

二进制表达式

二进制结果

十六进制结果

~0x41

~[0100 0001]

[1011 1110]

0xBE

~0x00

~[0000 0000]

[1111 1111]

0xFF

0x69&0x55

[0110 1001]&[0101 0101]

[0100 0001]

0x41

0x69|0x55

[0110 1001]|[0101 0101]

[0111 1101]

0x7D

位级运算的一个常见用法就是实现掩码运算,这里掩码是一个为模式,表示从一个字中选出的位的集合。

如:x = 0x89ABCDEF,x&0xFF = 0x000000EF

表达式 ~0 将生成一个全 1 的掩码,不管机器的字大小是多少。

2.1.8 C语言中的逻辑运算

|| OR 或

&& AND 与

! NOT 非

表达式

结果

!0x41

0x00

!0x00

0x01

!!0x41

0x01

0x69&&0x55

0x01

0x69||0x55

0x01

如果对第一个参数求值就能确定表达式的结果,那么逻辑运算符就不会对第二个参数求值。

2.1.9 C语言中的位移运算

左移:x<

逻辑右移:x>>k,在左端补k个0。

算术右移:x>>k,在左端补k个最高有效位的值。

2.2 整数表示

数学术语

下标w表示数据表示中的位数

符号

类型

含义

math?formula=B2T_w

函数

二进制转补码

math?formula=B2U_w

函数

二进制转无符号数

math?formula=U2B_w

函数

无符号数转二进制

math?formula=U2T_w

函数

无符号转补码

math?formula=T2B_w

函数

补码转二进制

math?formula=T2U_w

函数

补码转无符号数

math?formula=TMin_w

常数

最小补码值

math?formula=TMax_w

常数

最大补码值

math?formula=UMax_w

常数

最大无符号数

math?formula=%2B_w%5Et

操作

补码加法

math?formula=%2B_w%5Eu

操作

无符号数加法

math?formula=*_w%5Et

操作

补码乘法

math?formula=*_w%5Eu

操作

无符号数乘法

math?formula=-_w%5Et

操作

补码取反

math?formula=-_w%5Eu

操作

无符号数取反

2.2.1 整型数据类型

32位程序上C语言整型数据类型的典型取值范围

C数据类型

最小值

最大值

[signed]char

-128

127

unsigned char

0

255

short

-32 768

32 767

unsigned short

0

65 535

int

-2 147 483 648

2 147 483 647

unsigned

0

4 294 967 295

long

-2 147 483 648

2 147 483 647

unsigned long

0

4 294 967 295

int32_t

-2 147 483 648

2 147 483 647

uint32_t

0

4 294 967 295

int64_t

-9 223 372 036 854 775 808

9 223 372 036 854 775 807

uint_64_t

0

18 446 744 073 709 551 615

64位程序上C语言整型数据类型的典型取值范围

C数据类型

最小值

最大值

[signed]char

-128

127

unsigned char

0

255

short

-32 768

32 767

unsigned short

0

65 535

int

-2 147 483 648

2 147 483 647

unsigned

0

4 294 967 295

long

-9 223 372 036 854 775 808

9 223 372 036 854 775 807

unsigned long

0

18 446 744 073 709 551 615

int32_t

-2 147 483 648

2 147 483 647

uint32_t

0

4 294 967 295

int64_t

-9 223 372 036 854 775 808

9 223 372 036 854 775 807

uint_64_t

0

18 446 744 073 709 551 615

C语言的整型数据类型的保证的取值范围

C数据类型

最小值

最大值

[signed]char

-127

127

unsigned char

0

255

short

-32 767

32 767

unsigned short

0

65 535

int

-32 767

32 767

unsigned

0

65 535

long

-2 147 483 647

2 147 483 647

unsigned long

0

4 294 967 295

int32_t

-2 147 483 648

2 147 483 647

uint32_t

0

4 294 967 295

int64_t

-9 223 372 036 854 775 808

9 223 372 036 854 775 807

uint_64_t

0

18 446 744 073 709 551 615

2.2.2 无符号数的编码

无符号数编码的定义

对向量

math?formula=%5Cvec%7Bx%7D%3D%5Bx_%7Bw-1%7D%2Cx_%7Bw-2%7D%2C...%2Cx_0%5D%3A

math?formula=B2U_w(%5Cvec%7Bx%7D)%20%5Cdoteq%20%5Csum_%7Bi%3D0%7D%5E%7Bw-1%7Dx_i2%5Ei

math?formula=B2U_4(%5B0001%5D)%20%3D%200%20%C2%B7%202%5E3%20%2B%200%20%C2%B7%202%5E2%20%2B%200%20%C2%B7%202%5E1%20%2B%201%20%C2%B7%202%5E0%20%3D%200%20%2B%200%20%2B%200%20%2B%201%20%3D%201

math?formula=B2U_4(%5B0101%5D)%20%3D%200%20%C2%B7%202%5E3%20%2B%201%20%C2%B7%202%5E2%20%2B%200%20%C2%B7%202%5E1%20%2B%201%20%C2%B7%202%5E0%20%3D%200%20%2B%204%20%2B%200%20%2B%201%20%3D%205

math?formula=B2U_4(%5B1011%5D)%20%3D%201%20%C2%B7%202%5E3%20%2B%200%20%C2%B7%202%5E2%20%2B%201%20%C2%B7%202%5E1%20%2B%201%20%C2%B7%202%5E0%20%3D%208%20%2B%200%20%2B%202%20%2B%201%20%3D%2011

math?formula=B2U_4(%5B1111%5D)%20%3D%201%20%C2%B7%202%5E3%20%2B%201%20%C2%B7%202%5E2%20%2B%201%20%C2%B7%202%5E1%20%2B%201%20%C2%B7%202%5E0%20%3D%208%20%2B%204%20%2B%202%20%2B%201%20%3D%2015

最大值是用位向量[11...1]表示

math?formula=UMax_w%20%5Cdoteq%20%5Csum_%7Bi%3D0%7D%5E%7Bw-1%7D2%5Ei%20%3D%202%5Ew%20-%201

以4位情况为例,

math?formula=UMax_4%20%3D%20B2U_4(%5B1111%5D)%20%3D%202%5E4%20-%201%20%3D%2015

2.2.3 补码编码

补码编码的定义

对向量

math?formula=%5Cvec%7Bx%7D%3D%5Bx_%7Bw-1%7D%2Cx_%7Bw-2%7D%2C...%2Cx_0%5D%3A

math?formula=B2T_w(%5Cvec%7Bx%7D)%20%5Cdoteq%20-x_%7Bw-1%7D2%5E%7Bw-1%7D%2B%20%5Csum_%7Bi%3D0%7D%5E%7Bw-2%7Dx_i2%5Ei

math?formula=B2T_4(%5B0001%5D)%20%3D%20-%200%20%C2%B7%202%5E3%20%2B%200%20%C2%B7%202%5E2%20%2B%200%20%C2%B7%202%5E1%20%2B%201%20%C2%B7%202%5E0%20%3D%200%20%2B%200%20%2B%200%20%2B%201%20%3D%201

math?formula=B2T_4(%5B0101%5D)%20%3D%20-%200%20%C2%B7%202%5E3%20%2B%201%20%C2%B7%202%5E2%20%2B%200%20%C2%B7%202%5E1%20%2B%201%20%C2%B7%202%5E0%20%3D%200%20%2B%204%20%2B%200%20%2B%201%20%3D%205

math?formula=B2T_4(%5B1011%5D)%20%3D%20-%201%20%C2%B7%202%5E3%20%2B%200%20%C2%B7%202%5E2%20%2B%201%20%C2%B7%202%5E1%20%2B%201%20%C2%B7%202%5E0%20%3D%20-%208%20%2B%200%20%2B%202%20%2B%201%20%3D%20-%205

math?formula=B2T_4(%5B1111%5D)%20%3D%20-%201%20%C2%B7%202%5E3%20%2B%201%20%C2%B7%202%5E2%20%2B%201%20%C2%B7%202%5E1%20%2B%201%20%C2%B7%202%5E0%20%3D%20-%208%20%2B%204%20%2B%202%20%2B%201%20%3D%20-%201

最小值是用位向量[10...0]表示

(也就是设置这个位为负权,但是清除其他所有的位)

math?formula=TMin_w%20%5Cdoteq%20-2%5E%7Bw-1%7D

以4位情况为例,

math?formula=TMin_4%20%3D%20B2T_4(%5B1000%5D)%20%3D%20-2%5E3%20%3D%20-8

最大值是用位向量[01...1]表示

(清除具有负权的位,而设置其他所有的位)

math?formula=TMax_w%20%5Cdoteq%20%5Csum_%7Bi%3D0%7D%5E%7Bw-2%7D2%5Ei%20%3D%202%5E%7Bw-1%7D%20-%201

以4位情况为例,

math?formula=TMax_4%20%3D%20B2T_4(%5B0111%5D)%20%3D%202%5E2%20%2B%202%5E1%20%2B%202%5E0%20%3D%204%20%2B%202%20%2B%201%20%3D%207

注意:

1、补码的范围是不对称的:|TMin| = |TMax| + 1 ,也就是说,TMin没有与之对应的正数。

2、最大的无符号数值刚好比补码的最大值的两倍大一点:

math?formula=UMax_w%20%3D%202TMax_w%20%2B%201

3、-1 和 UMax 有同样的位表示——一个全 1 的串,数值 0 在两种表示方式中都是全 0 的串。

2.2.4 有符号数和无符号数之间的转换

补码转换为无符号数

对满足

math?formula=TMin_w%20%5Cleq%20x%20%5Cleq%20TMax_w

math?formula=x 有:

math?formula=T2U_w%20(x)%3D%5Cbegin%7Bcases%7D%20x%20%2B%202%5Ew%20%2C%5Cquad%20x%20%3C%200%20%5C%5C%20x%2C%5Cqquad%5Cquad%5C%20x%20%5Cgeq%200%20%5Cend%7Bcases%7D

比如,

math?formula=T2U_w(-12345)%3D-12345%2B2%5E%7B16%7D%3D53191,同时

math?formula=T2U_w(-1)%3D-1%2B2%5Ew%3DUMax_w

推导:补码转换为无符号数

math?formula=B2U_w(T2B_w(x))%3DT2U_w(x)%3Dx%2Bx_%7Bw-1%7D2%5Ew

根据上个公式的两种情况,在

math?formula=x的补码表示中,位

math?formula=x_%7Bw-1%7D决定了

math?formula=x是否为负。

无符号数转换为补码

对满足

math?formula=0%20%5Cleq%20x%20%5Cleq%20UMax_w

math?formula=%5C%20u%5C有:

math?formula=U2T_w%20(u)%3D%5Cbegin%7Bcases%7D%20u%20%2C%5Cqquad%5Cquad%5C%20u%20%5Cleq%20TMax_w%20%5C%5C%20u-2%5Ew%20%2C%5Cquad%20u%20%3E%20TMax_w%20%5Cend%7Bcases%7D

推导:无符号数转换为补码

math?formula=U2T_w(u)%3D-u_%7Bw-1%7D2%5Ew%2Bu

在u的无符号表示中,对上个公式的两种情况来说,位

math?formula=u_%7Bw-1%7D决定了 u 是否大于

math?formula=TMax_w%3D2%5E%7Bw-1%7D-1

2.2.5 C语言中的有符号数与无符号数

C语言支持所有整型数据类型的有符号和无符号运算。尽管C语言标准没有指定有符号数要采用某种表示,但是几乎所有机器都使用补码。通常,大多数数字都默认为是有符号的。要创建一个无符号常量,必须加上后缀字符 'U' 或者 'u' 。

C语言无符号数和有符号数之间的转换,一般原则是底层的位表示保持不变。

当执行一个运算时,如果它的一个运算数是有符号的而另一个是无符号的,那么C语言会隐式地将有符号参数强制类型转换为无符号数,并假设这两个数都是非负的,来执行这个运算。

C语言中TMin的写法

定义在头文件 limits.h 中

#define INT_MAX 2147483647

#define INT_MIN (-INT_MAX - 1)

2.2.6 扩展一个数字的位表示

要将一个无符号数转换为一个更大的数据类型,在开头添加0,这种运算被称为零扩展(zero extension)。

要将一个补码数字转换为一个更大的数据类型,可以执行一个符号扩展(sign extension),在表示中添加最高有效位的值。

2.2.7 截断数字

无符号数的截断结果是:

math?formula=B2U_k%5Bx_%7Bk-1%7D%2Cx_%7Bk-2%7D%2C...%2Cx_0%5D%3DB2U_w(%5Bx_%7Bw-1%7D%2Cx_%7Bw-2%7D%2C...%2Cx_0%5D)%5C%20mod%5C%202%5Ek

补码数字的截断结果是:

math?formula=B2T_k%5Bx_%7Bk-1%7D%2Cx_%7Bk-2%7D%2C...%2Cx_0%5D%3DU2T_k(B2U_w(%5Bx_%7Bw-1%7D%2Cx_%7Bw-2%7D%2C...%2Cx_0%5D))%5C%20mod%5C%202%5Ek

2.2.8 关于有符号数与无符号数的建议

有符号数到无符号数的隐式转换,是会导致错误或者漏洞的方式,避免这类错误的一种方法就是绝不使用无符号数。

2.3 整数运算

2.3.1 无符号加法

将操作

math?formula=%2B_w%5Eu 描述为无符号数加法

对满足

math?formula=0%20%5Cleq%20x%EF%BC%8Cy%20%5Cleq%202%5Ew

math?formula=%5C%20x%5C

math?formula=%5C%20y%5C有:

math?formula=x%2B_w%5Euy%3D%5Cbegin%7Bcases%7D%20x%2By%20%2C%5Cqquad%5Cquad%5C%20x%2By%3C2%5Ew%20%5Cqquad%5Cqquad%5C%20%E6%AD%A3%E5%B8%B8%20%5C%5C%20x%2By-2%5Ew%20%2C%5Cquad%202%5Ew%20%5Cleq%20x%2By%20%5Cleq%202%5E%7Bw%2B1%7D%20%5Cquad%20%E6%BA%A2%E5%87%BA%20%5Cend%7Bcases%7D

检测无符号数加法中的溢出

对在范围

math?formula=0%20%5Cleq%20x%EF%BC%8Cy%20%5Cleq%20UMax_w 中的

math?formula=%5C%20x%5C

math?formula=%5C%20y%5C,令

math?formula=s%20%5Cdoteq%20x%2B_w%5Euy。则对计算

math?formula=s,当且仅当

math?formula=s%20%3C%20x(或者等价地

math?formula=s%20%3C%20y)时,发生了溢出。

阿贝尔群

每个元素有一个加法逆元。考虑 w 位的无符号数的集合,执行加法运算

math?formula=%2B_w%5Eu。对于每个值 x,必然有某个值

math?formula=-_w%5Eux满足

math?formula=-_w%5Eux%2B_w%5Eux%3D0

无符号数求反

对满足

math?formula=0%20%5Cleq%20x%20%3C%202%5Ew 的任意

math?formula=x,其

math?formula=w位的无符号逆元

math?formula=-_w%5Eux由下式给出:

math?formula=-_w%5Eux%20%3D%5Cbegin%7Bcases%7D%20x%20%2C%5Cqquad%5Cquad%5C%20x%3D0%20%5C%5C%202%5Ew%20-%20x%20%2C%5Cquad%20x%3E0%20%5Cend%7Bcases%7D

2.3.2 补码加法

对满足

math?formula=-2%5E%7Bw-1%7D%20%5Cleq%20x%2Cy%20%5Cleq%202%5E%7Bw-1%7D-1 之内的整数

math?formula=x

math?formula=y,有:

math?formula=x%2B_w%5Ety%20%3D%5Cbegin%7Bcases%7D%20x%20%2B%20y%20-%202%5Ew%20%2C%5Cquad%202%5E%7Bw-1%7D%20%5Cleq%20x%20%2B%20y%20%5Cqquad%5Cqquad%5C%20%5C%20%5C%20%E6%AD%A3%E6%BA%A2%E5%87%BA%20%5C%5C%20x%20%2B%20y%20%2C%5Cqquad%5Cquad%20-2%5E%7Bw-1%7D%20%5Cleq%20x%20%2B%20y%20%5Cleq%202%5E%7Bw-1%7D%20%5Cquad%20%E6%AD%A3%E5%B8%B8%20%5C%5C%20x%20%2B%20y%20%2B%202%5Ew%20%2C%5Cquad%20x%20%2B%20y%20%3C%20-2%5E%7Bw-1%7D%20%5Cqquad%5Cqquad%20%E8%B4%9F%E6%BA%A2%E5%87%BA%20%5Cend%7Bcases%7D

两个数的

math?formula=w 位补码之和有完全相同的位级表示。我们可以按如下步骤表示运算

math?formula=%2B_w%5Et:将其参数转换为无符号数,执行无符号数加法,再将结果转换为补码:

math?formula=x%2B_w%5Ety%20%5Cdoteq%20U2T_w(T2U_w(x)%2B_w%5EuT2U_w(y))

检测补码加法中的溢出

对满足

math?formula=TMin_w%20%5Cleq%20x%EF%BC%8Cy%20%5Cleq%20TMax_w

math?formula=x

math?formula=y,令

math?formula=s%20%5Cdoteq%20x%20%2B_w%5Et%20y。当且仅当

math?formula=x%3E0%EF%BC%8Cy%3E0 ,但

math?formula=s%20%5Cleq%200 时,计算

math?formula=s 发生了正溢出。当且仅当

math?formula=x%3C0%EF%BC%8Cy%3C0,但

math?formula=s%20%5Cgeq%200 时,计算

math?formula=s 发生了负溢出。

2.3.3 补码的非

可以看到范围在

math?formula=TMin_w%20%5Cleq%20x%20%5Cleq%20TMax_w 中的每个数字

math?formula=x 都有

math?formula=%2B_w%5Et 下的加法逆元,我们将

math?formula=-_w%5Etx 表示如下:

对满足

math?formula=TMin_w%20%5Cleq%20x%20%5Cleq%20TMax_w

math?formula=x,其补码的非

math?formula=-_w%5Et 由下式给出

math?formula=-_w%5Etx%20%3D%5Cbegin%7Bcases%7D%20TMin_w%20%2C%5Cquad%20x%3DTMin_w%20%5C%5C%20-x%20%2C%5Cqquad%5Cquad%20x%3ETMin_w%20%5Cend%7Bcases%7D

也就是说,对

math?formula=w 位的补码加法来说,

math?formula=TMin_w 是自己的加法的逆,而对其他任何数值

math?formula=x 都有

math?formula=-x 作为其加法的逆。

补码非的位级表示

计算一个位级表示的值的补码非有几种聪明的方法。

1、执行位级补码非的第一种方法是对每一位求补,再对结果加1。在C语言中,我们可以说,对于任意整数值 x ,计算表达式 -x 和 ~x+1 得到的结果完全一样。

2、计算一个数 x 的补码非的第二种方法是建立在将位向量分为两部分的基础之上的。假设

math?formula=k 是最右边的 1 的位置,因而

math?formula=x 的位级表示形如

math?formula=%5Bx_%7Bw-1%7D%2Cx_%7Bw-2%7D%2C...%2Cx_%7Bk%2B1%7D%2C1%2C0%2C...%2C0%5D。(只要

math?formula=x%20%5Cne%200 就能够找到这样的

math?formula=k。)这个值的非写成二进制格式就是

math?formula=%5B~x_%7Bw-1%7D%2C~x_%7Bw-2%7D%2C...%2C~x_%7Bk%2B1%7D%2C1%2C0%2C...%2C0%5D。也就是,我们对位

math?formula=k 左边的所有位取反。

2.3.4 无符号乘法

对满足

math?formula=0%20%5Cleq%20x%2Cy%20%5Cleq%20UMax_w

math?formula=x

math?formula=y 有:

math?formula=x%20*_w%5Eu%20y%20%3D%20(x%20%5Ccdot%20y)%20%5C%20mod%5C%202%5Ew

2.3.5 补码乘法

对满足

math?formula=TMin_w%20%5Cleq%20x%2Cy%20%5Cleq%20TMax_w

math?formula=x

math?formula=y 有:

math?formula=x%20*_w%5Et%20y%20%3D%20U2T_w((x%20%5Ccdot%20y)%20%5C%20mod%5C%202%5Ew)

对于无符号和补码乘法来说,乘法运算的位级表示都是一样的。

2.3.6 乘以常数

乘以 2 的幂

math?formula=x 为位模式

math?formula=%5Bx_%7Bw-1%7D%2Cx_%7Bw-2%7D%2C...%2Cx_0%5D 表示的无符号整数。那么,对于任何

math?formula=k%20%5Cgeq%200,我们都认为

math?formula=%5Bx_%7Bw-1%7D%2Cx_%7Bw-2%7D%2C...%2Cx_0%2C0%2C...%2C0%5D 给出了

math?formula=x2%5Ek

math?formula=w%20%2B%20k 位的无符号表示,这里右边增加了

math?formula=k 个 0 。

与 2 的幂相乘的无符号乘法

C 变量 x 和 k 有无符号数值

math?formula=x

math?formula=k,且

math?formula=0%20%5Cleq%20k%20%3C%20w,则 C 表达式 x<

math?formula=x%20*_w%5Eu%202%5Ek

与 2 的幂相乘的补码乘法

C 变量 x 和 k 有补码值

math?formula=x 和 无符号数值

math?formula=k,且

math?formula=0%20%5Cleq%20k%20%3C%20w,则 C 表达式 x<

math?formula=x%20*_w%5Et%202%5Ek

由于整数乘法比移位和加法的代价要大得多,许多 C 语言编译器试图以移位、加法和减法的组合来消除很多整数乘以常数的情况。

对于某个常数 K 的表达式 x * K 生成代码。编译器会将 K 的二进制表示表达为一组 0 和 1 交替的序列:

math?formula=%5B(0...0)(1...1)(0...0)...(1...1)%5D

考虑一组从位位置 n 到位位置 m 的连续的 1(n

math?formula=%5Cgeqm)。我们可以用下面两种不同形式中的一种来计算这些位对乘积的影响:

形式A:(x<

形式B:(x<

把每个这样连续的 1 的结果加起来,不用做任何乘法,我们就能计算出 x * K 。

2.3.7 除以 2 的幂

整数除法要比整数乘法更慢。除以 2 的幂也可以用移位运算来实现。无符号和补码数分别使用逻辑移位和算术移位来达到目的。

整数除法总是舍入到零。它将

math?formula=%5Clfloor向下

math?formula=%5Crfloor舍入一个正值,而

math?formula=%5Clceil向上

math?formula=%5Crceil舍入一个负值。

除以 2 的幂的无符号除法

C 变量 x 和 k 有无符号数值

math?formula=x

math?formula=k,且

math?formula=0%20%5Cleq%20k%20%3C%20w,则 C 表达式 x >> k 产生数值

math?formula=%5Clfloor%20x%2F2%5Ek%20%5Crfloor

除以 2 的幂的补码除法,向下舍入

C 变量 x 和 k 有补码值

math?formula=x 和 无符号数值

math?formula=k,且

math?formula=0%20%5Cleq%20k%20%3C%20w,则当执行算术位移时, C 表达式 x >> k 产生数值

math?formula=%5Clfloor%20x%2F2%5Ek%20%5Crfloor

对于负数来说,向下舍入结果会有偏差。

除以 2 的幂的补码除法,向上舍入

C 变量 x 和 k 有补码值

math?formula=x 和 无符号数值

math?formula=k,且

math?formula=0%20%5Cleq%20k%20%3C%20w,则当执行算术位移时, C 表达式 (x+(1<> k 产生数值

math?formula=%5Clfloor%20x%2F2%5Ek%20%5Crfloor

2.3.8 关于整数运算的最后思考

计算机执行的“整数”运算实际上是一种模运算形式。

表示数字的有限字长限制了可能的值的取值范围,结果运算可能溢出。

补码表示提供了一种既能表示负数也能表示正数的灵活方法,同时使用了与执行无符号算术相同的位级实现。

无论运算数是以无符号形式还是以补码形式表示的,都有完全一样或者类似的位级行为。

2.4 浮点数

浮点表示对形如

math?formula=V%20%3D%20x%20%5Ctimes%202%5Ey 的有理数进行编码。它对执行涉及非常大的数字(|V|>>0)、非常接近于0(|V|<<1)的数字,以及更普遍地作为实数运算的近似值的计算,是很有用的。

2.4.1 二进制小数

形如

math?formula=b_mb_%7Bm-1%7D...b_1b_0.b_%7B-1%7Db_%7B-2%7D...b_%7B-n%2B1%7Db_%7B-n%7D 的表示法,其中每个二进制数字,或者称为位,

math?formula=b_i 的取值范围是 0 和 1 ,这种表示方法表示的数 b 定义如下:

math?formula=b%20%3D%20%5Csum_%7Bi%3D-n%7D%5E%7Bm%7Ds%5Ei%20%5Ctimes%20b_i

点左边的位的权是 2 的正幂,点右边的位的权是 2 的负幂。

例如,

math?formula=101.11_2 表示数字

math?formula=1%20%5Ctimes%202%5E2%20%2B%200%20%5Ctimes%202%5E1%20%2B%201%20%5Ctimes%202%5E0%20%2B%201%20%5Ctimes%202%5E%7B-1%7D%20%2B%201%20%5Ctimes%202%5E%7B-2%7D =

math?formula=4%20%2B%200%20%2B%201%20%2B%20%5Cfrac%7B1%7D%7B2%7D%20%2B%20%5Cfrac%7B1%7D%7B4%7D%20%3D%205%20%5Cfrac%7B3%7D%7B4%7D

形如

math?formula=0.11...1_2 的数表示的是刚好小于 1 的数。例如,

math?formula=0.111111_2 表示

math?formula=%5Cfrac%7B63%7D%7B64%7D,我们将用简单的表达法

math?formula=1.0-%5Cvarepsilon 来表示这样的数值。

定点表示法不能很有效的表示非常大的数字。

2.4.2 IEEE 浮点表示

IEEE浮点标准用

math?formula=V%20%3D%20(-1)%5Es%20%5Ctimes%20M%20%5Ctimes%202%5EE 的形式来表示一个数:

符号(sign)

math?formula=s 决定这数是负数(

math?formula=s%3D1)还是正数(

math?formula=s%3D0),而对于数值

math?formula=0 的符号位解释作为特殊情况处理。

尾数(significand)

math?formula=M 是一个二进制小数,它的范围是

math?formula=1%20%5Csim%202-%5Cvarepsilon,或者是

math?formula=0%20%5Csim%201-%5Cvarepsilon

阶码(exponent)

math?formula=E 的作用是对浮点数加权,这个权重是

math?formula=2

math?formula=E 次幂(可能是负数)。

将浮点数的位表示划分为三个字段,分别对这些值进行编码:

一个单独的符号位

math?formula=s 直接编码符号

math?formula=s

math?formula=k 位的阶码字段

math?formula=exp%20%3D%20e_%7Bk-1%7D...e_1e_0 编码阶码

math?formula=E

math?formula=n 位小数字段

math?formula=frac%20%3D%20f_%7Bn-1%7D...f_1f_0 编码尾数

math?formula=M,但是编码出来的值也依赖于阶码字段的值是否等于 0 。

在单精度浮点格式(C 语言中的 float)中,s、exp 和 frac 字段分别为

math?formula=1 位、

math?formula=k%3D8 位和

math?formula=n%3D23 位,得到一个 32 位的表示。

在双精度浮点格式(C 语言中的 double)中,s、exp 和 frac 字段分别为

math?formula=1 位、

math?formula=k%3D11 位和

math?formula=n%3D52 位,得到一个 64 位的表示。

ab3a675f908e?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

给定位表示,根据 exp 的值,被编码的值可以分成三种不同的情况(最后一种情况有两个变种)。图 2-33 说明了对单精度格式的情况。

ab3a675f908e?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

情况1:规格化的值

当 exp 阶码域的位模式既不全为 0,也不全为 1 时。

阶码字段被解释为以偏置(biased)形式表示的有符号整数。阶码的值是 ,其中 e 是无符号数,Bias 是一个等于 (单精度是127,双精度是1023)的偏置值。

小数字段 frac 被解释为描述小数值 ,其中 ,其二进制表示为 ,也就是二进制小数点在最高有效位的左边。

尾数定义为 。这种方式叫做隐含的以 1 开头的(implied leading 1)表示,因为可以把 看成一个二进制表达式为 的数字。

情况2:非规格化的值

当 exp 阶码域为全 0 时,所表示的数是非规格化形式。

阶码值是

math?formula=E%20%3D%201%20-%20Bias

尾数的值是

math?formula=M%20%3D%20f ,也就是小数字段的值,不包含隐含的开头的1。

非规格化数有两个用途:

1.提供了一种表示数值 0 的方法。

2.表示那些非常接近于 0.0 的数。

情况3:特殊值

当 exp 阶码域全为 1时,表示特殊值。

当小数域全为 0 时,得到的值表示无穷。当 s=0 时是

math?formula=%2B%5Cinfty,或者当 s=1 时是

math?formula=-%5Cinfty

当小数域为非零时,结果值被称为 “NaN”,即“不是一个数(Not a Number)”的缩写。

2.4.3 数字示例

图 2-35 展示了假定的 8 位浮点格式的示例,其中有

math?formula=k%3D4 的阶码位和

math?formula=n%3D3 的小数位。偏置量是

math?formula=2%5E%7B4-1%7D-1%3D7

ab3a675f908e?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

可以观察到最大非规格化数 和最小规格化数 之间的平滑转变。这种平滑性归功于我们对非规格化数的 E 的定义。通过将 E 定义为 1 - Bias ,而不是 -Bias ,我们可以补偿非规格化数的尾数没有隐含的开头的 1。

假如我们将图 2-35 中的值的位表达式解释为无符号整数,它们就是按升序排列的,就像它们表示的浮点数一样。这不是偶然的——IEEE格式如此设计就是为了浮点数能够使用整数排序函数来进行排序。

图 2-36 展示了一些重要的单精度和双精度浮点数的表示和数字值。依据图 2-35 中展示的 8 位格式,我们能够看出有 k 位阶码和 n 位小数的浮点表示的一般属性。

ab3a675f908e?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

值 +0.0 总有一个全为 0 的位表示。

最小的正非规格化值的位表示,是由最低有效位为 1 而其他所有位为 0 构成的。它具有小数(和尾数)值

math?formula=M%20%3D%20f%20%3D%202%5E%7B-n%7D 和阶码值

math?formula=E%20%3D%20-2%5E%7Bk-1%7D%20%2B%202。因此它的数字值是

math?formula=V%20%3D%202%5E%7B-n-2%5E%7Bk-1%7D%2B2%7D

最大的非规格化值的位模式是由全为 0 的阶码字段和全为 1 的小数字段组成的。它有小数(和尾数)值

math?formula=M%20%3D%20f%20%3D%201%20-%202%5E%7B-n%7D(我们写成

math?formula=1%20-%20%5Cvarepsilon)和阶码值

math?formula=E%20%3D%20-2%5E%7Bk-1%7D%2B2。因此,数值

math?formula=V%20%3D%20(1%20-%202%5E%7B-n%7D)%20%5Ctimes%202%5E%7B-2%5E%7Bk-1%7D%2B2%7D,这仅比最小的规格化值小一点。

最小的正规格化值的位模式的阶码字段的最低有效位为 1 ,其他位全为 0 。它的尾数值

math?formula=M%3D1,而阶码值

math?formula=E%3D-2%5E%7Bk-1%7D%2B2。因此,数值

math?formula=V%20%3D%202%5E%7B-2%5E%7Bk-1%7D%2B2%7D

值 1.0 的位表示的阶码字段除了最高有效位等于 0 以外,其他位都等于 1。它的尾数值是

math?formula=M%3D1,而它的阶码值是

math?formula=E%3D0

最大的规格化值的位表示的符号位为 0,阶码的最低有效位等于 0,其他位等于 1。它的小数值

math?formula=f%3D1-2%5E%7B-n%7D,尾数

math?formula=M%3D2-2%5E%7B-n%7D(我们写作

math?formula=2-%5Cvarepsilon)。它的阶码值

math?formula=E%3D2%5E%7Bk-1%7D-1,得到数值

math?formula=V%3D(2-2%5E%7B-n%7D)%20%5Ctimes%202%5E%7B2%5E%7Bk-1%7D-1%7D%20%3D%20(1-2%5E%7B-n-1%7D%20%5Ctimes%202%5E%7B2%5E%7Bk-1%7D%7D)

整数值转换成浮点形式,相关的区域对应于整数的低位,刚好在等于 1 的最高有效位之前停止(这个位就是隐含的开头的位 1),和浮点表示的小数部分的高位是相匹配的。

2.4.4 舍入

因为表示方法限制了浮点数的范围和精度,所以浮点运算只能近似地表示实数运算。因此,对于值x,能够找到“最接近的”匹配值x',它可以用期望的浮点形式表示出来。这就是舍入(rounding)运算的任务。

舍入方式有四种:

方式

1.40

1.60

1.50

2.50

-1.50

向偶数舍入

1

2

2

2

-2

向零舍入

1

1

1

2

-1

向下舍入

1

1

1

2

-2

向上舍入

2

2

2

3

-1

向偶数舍入是舍入到一个最接近的值。

2.4.5 浮点运算

浮点加法不具有结合性,例如,使用单精度浮点,表达式 (3.14+1e10)-1e10 求值得到 0.0 —— 因为舍入,值 3.14 会丢失。表达式 3.14+(1e10-1e10) 得出值 3.14。

浮点加法满足了单调性属性:如果

math?formula=a%20%5Cgeq%20b,那么对于任何 a、b 以及 x 的值,除了 NaN,都有

math?formula=x%2Ba%20%5Cgeq%20x%2Bb。无符号或补码加法不具有这个实数(和整数)加法的属性。

浮点乘法不具有结合性。例如,单精度浮点情况下,表达式 (1e201e20)1e-20 求值为

math?formula=%2B%5Cinfty,而 1e20(1e201e-20) 将得出 1e20。

浮点乘法在加法上不具备分配性。例如,单精度浮点情况下,表达式 1e20(1e20-1e20)求值为 0.0,而 1e201e20-1e20*1e20 会得出 NaN。

对于任何a、b和c,并且a、b和c都不等于 NaN,浮点乘法满足下列单调性:

math?formula=a%20%5Cgeq%20b%20%5C%20%E4%B8%94%5C%20c%20%5Cgeq%200%20%5CRightarrow%20a%20*%5C%20%5Efc%20%5Cgeq%20b%20*%5C%20%5Efc

math?formula=a%20%5Cgeq%20b%20%5C%20%E4%B8%94%5C%20c%20%5Cleq%200%20%5CRightarrow%20a%20*%5C%20%5Efc%20%5Cleq%20b%20*%5C%20%5Efc

只要

math?formula=a%20%5Cneq%20NaN,就有

math?formula=a%20*%20%5C%20%5Efc%20%5Cgeq%200

2.4.6 C 语言中的浮点数

所有的 C 语言版本提供了两种不同的浮点数据类型:float 和 double。

当程序文件中出现下列句子时,GUN 编译器 GCC 会定义程序常数 INFINITY(表示

math?formula=%2B%20%5Cinfty)和 NAN(表示 NaN):

#define _GUN_SOURCE 1

#include

当在 int、float 和 double 格式之间进行强制类型转换时,程序改变数值和位模式的原则如下(假设 int 是 32 位的):

从 int 转换成 float,数字不会溢出,但是可能被舍入。

从 int 或 float 转换成 double,因为 double 有更大的范围(也就是可表示值的范围),也有更高的精度(也就是有效位数),所以能够保留精确的数值。

从 double 转换成 float,因为范围要小一些,所以值可能溢出成

math?formula=%2B%5Cinfty

math?formula=-%5Cinfty。另外,由于精确度较小,它还可能被舍入。

从 float 或者 double 转换成 int,值将会向零舍入。例如,1.999 将被转换成 1,而 -1.999 将被转换成 -1。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值