2.1信息的位表示
-
为什么用十进制——适合人类使用
- 人类十个手指,历史渊源
-
为什么用二进制——适合机器使用
- 容易表示、储存,容易传输
-
位、字节 bit(比特)
- 计算机储存、处理的信息:二值信号
“ 位 ” 或 “ 比特 ”
- 计算机储存、处理的信息:二值信号
-
位组合
大多数计算机使用8位的块,或者字节(byte),作为最小的可寻址的内存单元,而不是访问内存中单独的位。
1 字节 = 8 bit 块
2.1.1进制
-
数的通用表示
-
二进制数
- 特点: 逢二进一,由0和1两个数码组成,基数为2,各个位权以2i表示。
- 优点:便于计算机储存、算术运算简单、支持逻辑运算
- MSB:最高有效位(Most Significant Bit)
LSB:最低有效位(Lowest Signtificant Bit)
- 缺点:数字串长、书写和阅读不便
-
十六进制数
- 基数为16,逢16进位,位权位16i ,16个数码
0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F
- 十六进制的加减运算
- 十六进制的加减运算类似十进制
逢16进1,借1当16
23D9H+94BEH=B897H
A59FH-62B8H=42E7H
二进制和十六进制之间有对应关系:
每4个二进制位对应1个16进制位
00111010B=3AH,F2H=11110010B
- 十六进制的加减运算类似十进制
- 优点:与二进制转换简单、阅读书写方便
- 基数为16,逢16进位,位权位16i ,16个数码
进制转换
-
十进制整数转换为k(2、8或16)进制数
- 整数转换:用除法—除基取余法
十进制整数部分不断除以基数k,并记下余数,直到商为0为止。
由最后一个余数起,逆向取各个余数,则为转换成的二进制或十六进制数。
126=01111110B 二进制数用后缀字母B
126=7EH 十六进制数用后缀字母H
- 整数转换:用除法—除基取余法
-
十进制小数转换为k进制数
- 小数转换:用乘法-乘基取整法
乘以基数k,记录整数部分,直到小数部分为0为止,
0.8125=0.1101B
0.8125=0.DH
- 小数转换:用乘法-乘基取整法
-
k进制转换为十进制
- 方法:按权展开
2、8、16进制之间的转换
- 方法:按权展开
2.1.2计算机内的数值表示——编码
-
需要考虑的问题
①编码的长度
②数的符号
③数的运算 -
字节值编码
- Byte=8bits
二进制(Binary):000000002-111111112
十进制(Decimal):010-25510
十六进制(Hexadecimal):0016-FF16
- Byte=8bits
-
C数据类型的宽度
c数据类型 | 32位 | 64位 | x86-64 |
---|---|---|---|
char | 1 | 1 | 1 |
short | 2 | 2 | 2 |
int | 4 | 4 | 4 |
long | 4 | 8 | 8 |
float | 4 | 4 | 4 |
double | 8 | 8 | 8 |
long double | – | – | 10/16 |
pointer | 4 | 8 | 8 |
2.2位级运算
2.2.1布尔代数(Boolean Algebra)
- Geroge Boole(1815-1864)提出逻辑代数的表示
- 逻辑值“True(真)”编码为1
- 逻辑值“False(假)”编码为0
- Claude Shannon(1916-2001)创立信息论
- 将布尔代数与数字逻辑联系起来
- 是数字系统设计与分析的重要工具
- 与 或 非
- 与(And)
当A=1并且B=1时,A&B=1
- 或(Or)
当A=1或者B=1时,A|B=1
- 非(Not)
当A=0时,~A=1
- 异或(Exclusion-Or,Xor)
当A=1或B=1且两者不同时为1,A^B=1
##2.2.2 一般的布尔代数(Operate on Bit Victors) - 位向量操作
- 按位操作
- 按位操作
- 布尔代数的全部性质均适用
2.2.3示例:集合的表示与运算
-
表示:位向量表示有限集合
- 宽度w个比特的位向量 [aw-1,…,a1,a0] 表示集合 {0,…,w-1} 的子集 A
- aj = 1 当且仅当 j ∈ A
- 0 11 0 1 00 1 {0,3,5,6}
7 65 4 3 21 0 - 0 1 0 1 0 1 0 1 {0,2,4,6}
7 6 5 4 3 2 1 0
- 0 11 0 1 00 1 {0,3,5,6}
-
运算
- & 交集
- |并集
- ^ 对称差集
- ~ 补集
##2.2.4 练习题
-
RGB Color Model
-
基于R(红色)G(绿色)B(蓝色)的开(1)和关(0),我们就能创建8种不同的颜色:
-
颜色中的每一种都可以用长度为3的位向量表示,从而将进行布尔运算
- 一种颜色的补通过关掉打开的电源同时打开关闭的电源形成
-
描述下面颜色的布尔运算结果:
Blue | Green = Cyan
Yellow & Cyan = Green
Red ^ Magenta = Blue
2.2.4C语言中的位级运算
1.C语言中的位运算:|,&, ~ ,^
- 适用于任何整数型类型: long,int,short,char,unsigned
- 将操作数视为位向量
- 将参数按位运算
2.例子(char类型)
- 巧用异或
- 按位异或是一种加的形式
- A ^ A = 0
2.2.5对比:C语言的逻辑运算
- c语言的逻辑运算符:&&,‖,!
- 将所有的 0 看成“False(假)”
- 将所有的 1 看成“True(真)”
- 计算结果总是 0 或 1
- 提前终止(Early termination)、短路求值(short cut)
2.2.6例子(char数据类型)
2.2.7 C语言中的移位运算
- 左移:x << y
- 将位向量x向左移动y位
- 扔掉左边多出(移出)的位
- 在右边补0
- 将位向量x向左移动y位
- 右移:x >> y
- 将位向量x向右移动y位
- 扔掉右边多出(移出)的位
- 逻辑右移:在左边补0
- 算术右移:复制左边的最高位(y位)
- 将位向量x向右移动y位
- 未明确定义
- 移位数量y < 0 或y≥x 的字长(位数)
2.3整型数
2.3.1表示有符号和无符号
1.整数编码(Encoding integers )
①无符号数
- 假设有一个整数数据类型有w位。我们可以将位向量写成向量x,表示整个向量,或者写成[xw-1,xw-2,…,x0],表示向量中的某一位。
- 把向量x看作一个2进制数。
- 获得无编码数
- 2w 为数字值的一部分
- 用一个函数B2Uw (Binary to Unsign 的缩写,长度为w)表示。
- 原理:
-
对向量x=[xw-1,xw-2,…,x0]:
-
在这个等式中,符号“”表示左边被定义为等于右边。函数U2Bw将长度为w的0,1串映射到非负整数。
-
示例
- 因此,函数B2Uw 能够被定义为一个映射B2Uw:{0,1}w→{2w-1}
-
- 无符号二进制数表示有一个很重要的属性,也就是每个介于0~2w-1之间的数都有唯一一个w位的编码值。
- 无符号数编码的唯一性
- 函数B2Uw 为一个双射
- 数学术语双射是指一个函数 f 有两面:它将数值x映射到y,即y = f (x),但它也可以反向操作,因为对每一个y而言,都有唯一一个数值x使得 f(x) = y,这可以用反函数 f-1 来表示,即x = f-1(y)。
- 函数B2Uw将每一个长度为w的位向量都映射为0 ~ 2w-1的唯一值;反过来,我们称U2Bw(即无符号函数到二进制),在0 ~ 2w-1之间的每一个整数,都可以映射一个唯一的长度为w的为模式。
②有符号数——补码(Two‘s complement)
- 用函数B2T表示位到补码的映射(Binary to Two’s complement)
- 用T2B-1表示补码的位模式
short int x : 15213
short int y : -15213
- c short :2字节
- 补号位
- 对于补码,最高位表示符号
- 0表示非负数(!=正数),1表示负数
- 对于补码,最高位表示符号
- 带符号数的表示及其运算
-
机器数与真值
- 机器数:最高位0表示正数,最高位1表示负数。
- 机器数:最高位0表示正数,最高位1表示负数。
-
原码
-
反码
- 正数的反码与其原码相同
- 负数的反码为:
其原码中符号不变,其余各位取反。
-
补码
- 正数的补码与其原码相同
(正数的原码,反码,补码均相同) - 负数的补码为:其反码的最低位加1。
示例:
- 正数的补码与其原码相同
-
2. 数值范围
- 无符号数值(U)
- UMin = 0
000…0 - UMax = 2w - 1
111…1
- UMin = 0
- 补码数值(T)
- TMin = -2w-1
100…0 - TMax =2w-1 - 1
011…1 - -1
111…1
- TMin = -2w-1
-
不同字长的数值
-
观察
- |TMin| = TMax + 1
非对称 - UMax = 2 * TMax + 1
- |TMin| = TMax + 1
-
C语言的常量声明
- #include <limits.h>
- #define INT_MAX 2147483647
- #define INT_MIN (-INT_MAX-1)
- #define UINT_MAX 0xffffffff
- 平台相关
- #indefine ULANG_MAX
- #define ULONG_MAX
- #define UlONG_MIN(-LONG_MAX-1)
- #include <limits.h>
-
无符号数与有符号数编码的值
- 相同
- 非负数的编码相同
- 单值性
- 每个位模式对应一个唯一的整数值
- 每个可描述整数有一个唯一编码
- →有逆映射
- U2B(x) = B2U-1(x)
- 无符号整数的位模式
- T2B(x)= B2T-1(x)
- 补码的位模式
- 相同
2.3.2无符号数和有符号数的转换
- C语言允许在各种不同的数字数据类型之间做强制类型转换。
- 转换规则:
位模式不变、数值可能改变(按不同编码规则重新解读)
3.有无符号数的转换
- 正数部分直接转换
- 负数部分
- 关系
- 由上图可以看出,-12345和53191的位模式都是一样的,只是这两个数对位模式的解释不同。
- 因此,转换的关键是注意位编码的高位在解释正负号时的变化
- 关系
- 转换的可视化
- 补码→无符号数
- 顺序导致
- 负数→大整数
4.基本原则
- 位模式不变
- 重新解读
- 会有意外作用:数值被 + 或 -2w
- 当表达式同时含有无符号数和有符号数时
- 有符号数被转换为无符号数
- 当心副作用
##2.3.3 C语言中的有符号数和无符号数
- C语言支持所有整型数据类型的有无符号数运算。尽管C语言标准没有制定有符号数要采用某种表示,但几乎所有的机器都使用补码。通常,大多数数字都默认为是有符号的。例如,当声明一个像12345或0x1A2B这样的常量时,这个值就被认为是有符号的。要创建一个无符号常量,必须加上字符“U”或“u”,例如,12345U或0x1A2Bu。
- C语言允许有无符号之间的运算。虽然C语言标准没有精确规定应该如何进行这种转换,但大多数系统遵循的原则是底层的位表示不变。因此,在一台采用补码的机器上,当从无符号数转换为有符号数时,效果就是应用函数U2Tw,而从有符号转换为无符号时,就是应用函数T2Uw,其中 w 表示数据类型的位数。
2.3.3扩展、截断
1.符号扩展
①任务:
- 给定w位的有符号整型数x
- 将其转换为w+k位的相同数值的整型数
②规则:
- 将最高有效位(符号位)xw-1复制k份:
- 示意图
③符号扩展示例
- 从短整数类型向长整数类型转换时,C语言自动进行符号扩展。
④总结扩展、截断的基本规则
- 扩展(例如从short int 到 int 的转换)
- 无符号数:填充0
- 有符号数:符号扩展
- 结果都是明确的预期值
- 截断(例如从unsigned 到unsigned short的转换)
- 无论有/无符号数:多出的位均被截断
- 结果重新解读
- 无符号数:相当于求模运算
- 有符号数:与求模运算相似
- 对于小整数,结果是明确的预期值
2.3.4总结
- 我们看到了许多无符号运算的细微特性,尤其是有符号数到无符号数的隐式转换,会导致错误或者漏洞的方式。避免这类错误的方法就是绝不使用无符号数。实际上,除了C之外很少有语言支持无符号整数。很明显,这些语言的设计者认为它们带来的麻烦要比益处多得多。
- 但当我们想要把字仅仅看做是位的集合而没有任何数字意义时,无符号数值是非常有用的。
2.3.5 整数运算:加,非,乘,移
1.无符号数加减
- 许多刚入门的程序员非常惊奇的发现,两个正数相加会得到一个负数,而比较表达式x>y和比较表达式x-y<0会产生不同的结果。这些属性都是由于计算机运算的有限性造成的。
①无符号加法
- 操作数:w位
- 真实和:w+1位
- 丢弃进位:w位
- 示意图
- 标准加法功能
- 忽略进位输出
- 模数加法:相当于一个模运算
②整数加法可视化示意图
-
整数加法
- 4-bit整型数x,y
- 计算真实值Add4(x,y)
- 和随x和y线性增加
- 表面为斜面形
③无符号数加法可视化
- 数值面有弯折
- 当真实和≥2w时溢出
- 最多溢出一次
2.补码加法
- 操作数:w位
- 真实和:w+1位
- 丢弃进位:w位
- 示意图
①Tadd和Uadd具有完全相同的位级表现
-
C语言中有符号数(补码)与无符号数加法:
int s, t, x, y; s = (int)((unsigned)x+(unsigned)y); t = x + y
-
将会有s == t
②规则
- 真实和需要w+1位
- 丢弃最高有效位(MSB)
- 将剩余的位视作补码(整数)
③补码加法的溢出问题
- 补码加法的可视化表示
- 数值
- 4位补码
- 数值范围-8~+7
- 弯折一一溢出
- x + y ≥ 2w-1 时
- 变成负数
- 最多一次
- x + y < 2w-1 时
- 变成正数
- 最多一次
- x + y ≥ 2w-1 时
- 数值
3.乘法
- 目标:计算w位的两个数x和y的乘积
- 有符号数或无符号数
- 乘积的结果可能超过w位
- 乘积的无符号数最多可达2w位
- 结果范围:0 ≤ x * y ≤ (2w-1)2=22w -2w+1+1
- 补码的最小值(负数)最多需要2w-1位
- 结果范围:x * y ≥ (-2w-1) *(2w-1-1) = - 22w-2 + 2w-1
- 补码最大值(正数)最多需要2w位——值位(Tminw)2
- 结果范围: x * y ≤(-2w-1)2 = 22w-2
- 乘积的无符号数最多可达2w位
- 为了获得精确结果可拓展乘积的字长
- 在需要时使用软件方法完成,例如:算术程序包“arbtrary precision”
①C语言的无符号乘法
- 操作数:w位
- 真实乘积:2w位
- 丢弃w位:保留低位w位
- 忽略高w位
- 相当于对于乘积执行了模运算
UMultw(x , y)=x * y mod 2w
②C语言的有符号乘法
-
- 操作数:w位
-
真实乘积:2w位
-
丢弃w位:保留低位w位
-
忽略高w位
-
与无符号数乘不同之处
- 乘积的符号扩展
-
乘积的低位相同
③用移位实现乘以2的幂
- 无论有符号数还是无符号数:
u << k
可得到 u * 2k - 操作数:w 位
- 真实乘积:w + k 位
- 丢弃高 k 位:保留低 w 位
- 示例
- u << 3 == u * 8
- ( u << 5 ) - ( u << 3 ) == u * 24
- 绝大多数机器,移位比乘法快
- 编译器自动生成基于移位的乘法代码
④用移位实现乘以2的幂
- 无符号数“除以2的幂”的商
- u >> k 得到(u / 2k)
- 使用逻辑右移
- 操作数
- 除法
- 结果
- 示例