Post

数的表示

数的表示

溢出(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应该表示为\(1_{10}=001_{2}\)
  2. $-1 + 1 = 0$
  3. 我们希望加法按照熟悉的方式工作

考虑3位数字系统:

  • $001 + “-1” = 0$
  • $001 + 111 = 1000 = 000$(因为溢出)

所以我们发现:”-1”可以用111表示!

负数的表示

如果111表示-1,那么-2应该是什么?

  • $-2 = -1 - 1$
  • 所以$-2 = 111 - 001 = 110$

我们来验证这个结果:

  • $-2 + 2$ 应该等于0:
\[110 + 010 = 1000 = 000 ✓\]
  • -2 + 5 应该等于3:
\[110 + 101 = 1011 = 011 ✓\]

找到-x的简单方法

给定一个非负二进制数(比如用4位表示的5:0101),找到它的负数的步骤:

  1. 将所有位取反:

    \(5_{10}=0101_{2}→ 1010\)(这是5的”一补数”)

  2. 加1:

    $1010 + 1 = 1011$(这是5的”二补数”)

验证:$0101 + 1011 = 10000 = 0000$(溢出后为0)

所以得到公式:$-x = \tilde x + 1$(其中~表示取反)

为什么这样做有效?

  1. 考虑任何数和它的One’s Complement:

    • 0101
    • 1010

    它们互为补数是因为对应位是互补的。相加时,所有位都会变成1: \(0101 + 1010 = 1111\)

  2. 当一个数和它的补数相加后再加1,必然因为溢出而得到0: \((0101 + 1010) + 1 = 1111 + 1 = 10000 = 0000\)

  3. 如果$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”开头
  2. 最高位的”1”可以看作是引入了一个负值
    • 例如:$101 = 1×(-4) + 0×2 + 1×1 = -3$
    • 而:$010 = 0×(-4) + 1×2 + 0×1 = 2$
  3. 最小的负数(在这个例子中是-4)没有对应的正数

整数编码:紧凑形式

基本概念

1
2
short int x = 15213;
short int y = -15213;

编码规范

  • C语言不强制要求使用二进制补码

    但绝大多数现代计算机系统采用此方案,本文默认使用该方式

  • C short类型

    占2字节(16位)存储空间

二进制补码原理

符号位机制

  • 最高有效位(MSB)作为符号位

    • 0 表示非负数
    • 1 表示负数
  • 转换公式

    • 无符号数:
\[B2U(X) = \sum_{i=0}^{w-1} x_i \cdot 2^i\]
  • 二进制补码:
\[B2T(X) = -x_{w-1} \cdot 2^{w-1} + \sum_{i=0}^{w-2} x_i \cdot 2^i\]

数值示例

类型 十进制值 十六进制 二进制表示
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. 非对称范围

    负数范围比正数多1个值(包含TMin)

  2. -1的特殊表示

    全1的位模式统一表示-1

  3. 溢出行为

    • 超过TMax会下溢到TMin
    • 低于TMin会上溢到TMax
  4. 零的唯一性

    仅有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

重要提示:进行位操作时需注意符号扩展问题,建议使用无符号类型进行位运算

该编码方案在计算机体系结构中广泛应用,理解其原理对处理底层数据表示、内存操作和优化计算性能至关重要。

This post is licensed under CC BY 4.0 by the author.