数据表示与运算
📅 2026/8/1 1:55:08
👁️ 阅读次数
📝 编程学习
数据表示与运算
🎯核心目标:掌握计算机如何用二进制表示各种数据(整数、小数、字符),以及ALU如何进行算术和逻辑运算。
一、为什么计算机使用二进制?
💡核心原因:二进制最简单、最可靠、最物理可实现。
| 原因 | 解释 |
|---|---|
| 物理实现简单 | 电路只需两种状态:高电平(1) / 低电平(0),可用电压、电流、磁化方向表示 |
| 运算规则简单 | 二进制加法只有4种组合,比十进制简单得多 |
| 抗干扰能力强 | 两种状态区分度大,即使信号有一定衰减也能正确识别 |
| 逻辑门实现方便 | 与布尔代数完美对应:真/假 = 1/0 |
🔑关键认识:计算机内部所有数据——数字、文字、图像、音频、视频——本质上都是0和1的序列。
二、进制转换
1. 常见进制
| 进制 | 前缀/后缀 | 示例 | 应用场景 |
|---|---|---|---|
| 二进制 | 0b/B | 0b1010 | 计算机内部 |
| 八进制 | 0/O | 017 | Unix文件权限 |
| 十进制 | 无 /D | 13 | 人类日常 |
| 十六进制 | 0x/H | 0xFF | 内存地址、颜色编码 |
2. R进制 ↔ 十进制
R进制 → 十进制:按权展开求和
(1011.01)₂ = 1×2³ + 0×2² + 1×2¹ + 1×2⁰ + 0×2⁻¹ + 1×2⁻² = 8 + 0 + 2 + 1 + 0 + 0.25 = 11.25十进制 → R进制:整数部分除R取余(倒序),小数部分乘R取整(正序)
将 13.625 转为二进制: 整数部分:13 ÷ 2 = 6 余 1 6 ÷ 2 = 3 余 0 3 ÷ 2 = 1 余 1 1 ÷ 2 = 0 余 1 → 倒序:1101 小数部分:0.625 × 2 = 1.25 → 取整 1 0.25 × 2 = 0.5 → 取整 0 0.5 × 2 = 1.0 → 取整 1 → 正序:101 结果:(13.625)₁₀ = (1101.101)₂⚠️注意:有些小数无法精确转换(如0.1),会在有限位数后截断,导致浮点数精度问题。
3. 二进制 ↔ 八进制/十六进制
- 二进制→八进制:每3位一组(整数左补零,小数右补零)
- 二进制→十六进制:每4位一组
(110101110.101)₂ → 八进制:110 101 110 . 101 → (656.5)₈ → 十六进制:0001 1010 1110 . 1010 → (1AE.A)₁₆三、定点数的表示
定点数:小数点位置固定。分为定点整数和定点小数。
1. 真值与机器数
- 真值:实际数值(带正负号),如 +13、-7.5
- 机器数:计算机中存储的二进制表示(用符号位表示正负)
2. 无符号数
- 所有位都表示数值,没有符号位
- 8位无符号数范围:0 ~ 255(0 ~ 2⁸-1)
- n位无符号数范围:0 ~ 2ⁿ-1
3. 有符号数的表示方法
原码(Sign-Magnitude)
- 最高位为符号位(0正1负),其余为数值位
- 例:+5 =
0,0000101,-5 =1,0000101 - 缺点:零有两种表示(+0和-0);加减运算需要额外判断符号
反码(One’s Complement)
- 正数:与原码相同
- 负数:符号位不变,数值位按位取反
- 例:-5 =
1,1111010 - 缺点:零仍有两种表示;运算时需处理循环进位
补码(Two’s Complement)⭐⭐⭐
🔑最重要、最常用:现代计算机中,整数统一使用补码表示和运算。
- 正数:与原码相同
- 负数:反码 + 1(或:模 - 真值)
- 例:-5 的补码:原码
1,0000101→ 反码1,1111010→ 补码1,1111011 - 优点:零唯一表示;加减法统一;符号位参与运算无需单独处理
补码求法速记:
方法1:原码 → 反码(除符号位取反) → +1 方法2:从右往左,找到第一个1,它左边所有位取反(符号位除外) 方法3:模运算(2ⁿ - |真值|)移码(Excess-N / Offset Binary)
- 用于浮点数的阶码表示
- 定义:移码 = 真值 + 偏移量(通常为 2ⁿ⁻¹)
- 特点:便于比较大小(与无符号数的顺序一致)
- 例:8位移码,偏移量128,+5 =
10000101(即128+5=133)
4. 三种表示法的对比
| 真值 | 原码 | 反码 | 补码 |
|---|---|---|---|
| +5 | 0,0000101 | 0,0000101 | 0,0000101 |
| -5 | 1,0000101 | 1,1111010 | 1,1111011 |
| +0 | 0,0000000 | 0,0000000 | 0,0000000 |
| -0 | 1,0000000 | 1,1111111 | 0,0000000(与+0相同)✓ |
5. 定点数的范围
n位整数(1位符号 + n-1位数值):
- 原码/反码:-(2ⁿ⁻¹-1) ~ +(2ⁿ⁻¹-1)
- 补码:-2ⁿ⁻¹ ~ +(2ⁿ⁻¹-1)
- 无符号:0 ~ 2ⁿ-1
8位举例:
- 补码:-128 ~ +127
- 原码:-127 ~ +127
- 无符号:0 ~ 255
💡补码可以多表示一个负数:因为零唯一,所以-128的补码是
10000000。
四、定点数的运算
1. 移位运算
| 移位类型 | 操作 | 符号位 | 空位填充 | 用途 |
|---|---|---|---|---|
| 逻辑左移 | 整体左移 | 参与 | 右补0 | 无符号数×2 |
| 逻辑右移 | 整体右移 | 参与 | 左补0 | 无符号数÷2 |
| 算术左移 | 整体左移 | 不变 | 右补0 | 有符号数×2 |
| 算术右移 | 整体右移 | 不变 | 左补符号位 | 有符号数÷2 |
⚠️算术左移注意:若符号位改变,则发生溢出!
2. 加减运算(补码加减法)⭐⭐⭐
核心公式:
[A + B]补 = [A]补 + [B]补 (mod 2ⁿ) [A - B]补 = [A]补 + [-B]补 = [A]补 + [B]补的变补 (mod 2ⁿ)💡减法变加法:减去一个数 = 加上它的相反数的补码。这样硬件只需实现加法器!
溢出判断:
- 单符号位:两个正数相加得负,或两个负数相加得正 → 溢出
- 双符号位:运算结果的两个符号位不同 → 溢出(
01正溢,10负溢) - 进位判断:最高数值位进位 ⊕ 符号位进位 = 1 → 溢出
3. 乘法运算
原码乘法
- 符号位单独处理(异或),数值部分做绝对值乘法
- 类似十进制竖式乘法:累加+移位
补码乘法(Booth算法)⭐
- 直接处理补码,无需先转原码
- 核心思想:利用相邻位的变化来决定加/减/不移位
- 适合硬件实现,高效
乘法溢出
- n位数乘n位数,结果可能2n位
- 若只保留n位,高位丢失 → 溢出
4. 除法运算
原码除法
- 符号位单独处理,数值做绝对值除法
补码除法(加减交替法)
- 直接处理补码
- 核心:根据余数符号决定加除数还是减除数
5. 标志位(条件码)
运算结果会设置标志位,供条件判断使用:
| 标志位 | 名称 | 含义 |
|---|---|---|
| ZF | 零标志 | 结果全为0 |
| SF | 符号标志 | 结果最高位(补码即符号位) |
| CF | 进位/借位标志 | 无符号数运算溢出 |
| OF | 溢出标志 | 有符号数运算溢出 |
| PF | 奇偶标志 | 结果低8位中1的个数为偶数 |
| AF | 辅助进位 | 低4位向高4位进位 |
五、浮点数的表示(IEEE 754标准)⭐⭐⭐
🔑 浮点数 = 科学计数法的二进制版:N = (-1)ˢ × 1.M × 2ᴱ⁻¹²⁷
1. 浮点数的格式
┌───┬──────────┬──────────────────────────────┐ │ S │ E │ M (尾数/有效数) │ │ 1位│ 指数位 │ 尾数位 │ └───┴──────────┴──────────────────────────────┘| 精度 | 总位数 | 符号位 | 指数位 | 尾数位 | 偏移量 |
|---|---|---|---|---|---|
| 单精度(float) | 32 | 1 | 8 | 23 | 127 |
| 双精度(double) | 64 | 1 | 11 | 52 | 1023 |
2. 规格化表示
- 尾数最高位恒为1(隐含),所以实际精度比位数多1
- 单精度:实际精度24位;双精度:实际精度53位
- 规格化范围:1 ≤ |M| < 2
3. 特殊值
| 指数(E) | 尾数(M) | 含义 |
|---|---|---|
| 全0 | 全0 | ±0 |
| 全0 | 非0 | 非规格化数(非常小的数) |
| 1~254 | 任意 | 规格化数 |
| 全1 | 全0 | ±∞(正负无穷) |
| 全1 | 非0 | NaN(非数字) |
4. 浮点数运算步骤
- 对阶:将指数较小的数调整为与较大的数相同(尾数右移)
- 尾数运算:加减
- 规格化:确保尾数在 [1, 2) 范围内
- 舍入:按规则截断或进位
- 溢出判断:阶码是否超出范围
⚠️经典陷阱:
0.1 + 0.2 ≠ 0.3(在浮点数中),因为0.1无法精确表示为二进制浮点数!
六、字符与字符串的编码
1. ASCII编码
- 7位编码,128个字符(0~127)
- 包括:控制字符(0~31)、数字、字母、标点
- 扩展ASCII:8位,256个字符(含欧洲语言符号)
2. Unicode
- 统一字符编码,覆盖全球所有语言
- UTF-8:变长编码(1~4字节),兼容ASCII,互联网主流
- UTF-16:变长编码(2或4字节),Java/C#内部使用
- UTF-32:定长4字节
3. 汉字的表示
- GB2312:6763个汉字,双字节
- GBK:21003个汉字,向下兼容GB2312
- GB18030:27484个汉字,兼容GBK
- Unicode:统一编码,CJK统一汉字
4. 字符串的存储
- C语言:以
\0结尾的字符数组 - 其他方式:长度前缀 + 字符数组
- 大端/小端:多字节字符在内存中的字节顺序
5. 大端 vs 小端 ⭐
| 方式 | 含义 | 举例(0x12345678) |
|---|---|---|
| 大端(Big-Endian) | 高位字节存放在低地址 | 12 34 56 78(人类阅读习惯) |
| 小端(Little-Endian) | 低位字节存放在低地址 | 78 56 34 12(x86架构) |
💡记忆法:大端 = 大端在前(高位在前);小端 = 小端在前(低位在前)。x86 CPU是小端,网络传输通常大端。
七、校验码
1. 奇偶校验码
- 在数据后添加1位校验位,使1的个数为奇数(奇校验)或偶数(偶校验)
- 只能检测奇数个位错误,不能纠错
2. 海明码(Hamming Code)
- 可检测并纠正1位错误
- 核心:在2的幂次位置(1,2,4,8…)放置校验位
- 校验位覆盖特定位置的数据位
- 若需检测d位错、纠正c位错,需满足:2ʳ ≥ d + c + 1(r为校验位位数)
3. 循环冗余校验(CRC)⭐
- 用于数据传输和存储的检错
- 将数据视为多项式,除以生成多项式,余数作为校验码
- 检错能力强,能检测多位错误和突发错误
- 硬件实现简单(移位寄存器+异或门)
八、重点回顾
| 重点 | 一句话总结 |
|---|---|
| 进制转换 | 整数除R取余,小数乘R取整;二-八进制3位一组,二-十六进制4位一组 |
| 原码 | 符号位+数值位,零不唯一,加减复杂 |
| 反码 | 负数数值取反,零不唯一,需循环进位 |
| 补码 | 负数反码+1,零唯一,加减统一,符号位参与运算 |
| 移码 | 真值+偏移量,用于浮点阶码,便于比较 |
| 浮点数 | IEEE 754:符号位 + 阶码 + 尾数,注意规格化和特殊值 |
| 大小端 | 大端高位在前(网络),小端低位在前(x86) |
| 校验码 | 奇偶校验(检错)、海明码(纠错)、CRC(强检错) |
📌下章预告:我们将探索计算机的"记忆系统"——存储器层次结构。从高速昂贵的寄存器到海量便宜的磁盘,了解Cache如何让你的程序飞起来!
编程学习
技术分享
实战经验