数的表示
溢出(Overflow)
首先让我们考虑一个简单的3位二进制数字序列:
1
2
十进制:0 1 2 3 4 5 6 7
二进制:000 001 010 011 100 101 110 111
当我们尝试给\(7_{10}=111_{2}\)加1时会发生什么?
在二进制中,$111 + 001 = 1000$。但因为我们只能存储3位,所以最高位的1会被丢失。这就是所谓的溢出(overflow),最终结果变成了000。
模运算(Modulus Arithmetic)
让我们进一步探索溢出的概念。在3位系统中:
1
2
3
4
5
6
7
111 + 001 = 1000 = 000(丢失最高位)
111 + 010 = 1001 = 001
111 + 011 = 1010 = 010
111 + 100 = 1011 = 011
...
111 + 110 = 1101 = 101
111 + 111 = 1110 = 110
这表明当数字变得”太大”时,算术运算会”环绕”回来。
无符号整数和非负整数
- “ints”指的是我们可以用固定位数(bit width)表示的有限整数集合。
- 无符号整数通常使用简单的二进制表示:
- “unsigned”整数是指在数字序列中不包含负数的整数
- 非负数是指在包含负数的数字序列中大于等于0的数
如何表示负数?
最初的想法是使用最高位作为符号位:
- 0表示非负数
- 1表示负数
例如在3位系统中:
1
2
二进制:000 001 010 011 100 101 110 111
数值: 0 1 2 3 -0 -1 -2 -3
这种方法的优点:
- 可以表示正负数
- 0有一个表示方法
缺点:
- 存在-0,虽然它在数值上等于0,但表示不同
- 加法运算变得复杂。例如,$1 + (-1)$应该等于0,但$001 + 101 = 110$,这反而等于-2
一个巧妙的技巧
让我们从三个基本需求出发:
- 1应该表示为\(1_{10}=001_{2}\)
- $-1 + 1 = 0$
- 我们希望加法按照熟悉的方式工作
考虑3位数字系统:
- $001 + “-1” = 0$
- $001 + 111 = 1000 = 000$(因为溢出)
所以我们发现:”-1”可以用111表示!
负数的表示
如果111表示-1,那么-2应该是什么?
- $-2 = -1 - 1$
- 所以$-2 = 111 - 001 = 110$
我们来验证这个结果:
- $-2 + 2$ 应该等于0:
- -2 + 5 应该等于3:
找到-x的简单方法
给定一个非负二进制数(比如用4位表示的5:0101),找到它的负数的步骤:
-
将所有位取反:
\(5_{10}=0101_{2}→ 1010\)(这是5的”一补数”)
-
加1:
$1010 + 1 = 1011$(这是5的”二补数”)
验证:$0101 + 1011 = 10000 = 0000$(溢出后为0)
所以得到公式:$-x = \tilde x + 1$(其中~表示取反)
为什么这样做有效?
-
考虑任何数和它的One’s Complement:
- 0101
- 1010
它们互为补数是因为对应位是互补的。相加时,所有位都会变成1: \(0101 + 1010 = 1111\)
-
当一个数和它的补数相加后再加1,必然因为溢出而得到0: \((0101 + 1010) + 1 = 1111 + 1 = 10000 = 0000\)
-
如果$x + y = 0$,那么y一定等于-x
Two’s Complement的可视化
数字在二补数系统中是这样排列的:
1
2
二进制:000 001 010 011 100 101 110 111
数值: 0 1 2 3 -4 -3 -2 -1
需要注意的特点:
- 所有负数都以”1”开头
- 最高位的”1”可以看作是引入了一个负值
- 例如:$101 = 1×(-4) + 0×2 + 1×1 = -3$
- 而:$010 = 0×(-4) + 1×2 + 0×1 = 2$
- 最小的负数(在这个例子中是-4)没有对应的正数
整数编码:紧凑形式
基本概念
1
2
short int x = 15213;
short int y = -15213;
编码规范
-
C语言不强制要求使用二进制补码
但绝大多数现代计算机系统采用此方案,本文默认使用该方式
-
C short类型
占2字节(16位)存储空间
二进制补码原理
符号位机制
-
最高有效位(MSB)作为符号位
0
表示非负数1
表示负数
-
转换公式
- 无符号数:
- 二进制补码:
数值示例
类型 | 十进制值 | 十六进制 | 二进制表示 |
---|---|---|---|
x | 15213 | 0x3B6D | 00111011 01101101 |
y | -15213 | 0xC493 | 11000100 10010011 |
符号位高亮显示:
- x的符号位为
0
(正数)- y的符号位为
1
(负数)
数值范围界限
无符号整数范围
边界 | 计算公式 | 16位示例值 | 二进制模式 |
---|---|---|---|
UMin | 0 | 0 | 00000000 00000000 |
UMax | $2^w -1$ | 65535 | 11111111 11111111 |
二进制补码范围
边界 | 计算公式 | 16位示例值 | 二进制模式 |
---|---|---|---|
TMin | $-2^{w-1}$ | -32768 | 10000000 00000000 |
TMax | $2^{w-1}-1$ | 32767 | 01111111 11111111 |
-1 | 特殊值 | -1 | 11111111 11111111 |
关键特性分析
-
非对称范围
负数范围比正数多1个值(包含TMin)
-
-1的特殊表示
全1的位模式统一表示-1
-
溢出行为
- 超过TMax会下溢到TMin
- 低于TMin会上溢到TMax
-
零的唯一性
仅有
00000000 00000000
表示0
编程注意事项
1
2
3
4
5
6
7
8
// 检测符号位示例
int is_negative(short num) {
return (num >> 15) & 1; // 右移15位获取符号位
}
// 边界值验证
assert(SHRT_MAX == 32767); // TMax
assert(SHRT_MIN == -32768); // TMin
重要提示:进行位操作时需注意符号扩展问题,建议使用无符号类型进行位运算
该编码方案在计算机体系结构中广泛应用,理解其原理对处理底层数据表示、内存操作和优化计算性能至关重要。