计算机体系结构 01:比特怎样表示数
计算机体系结构 01:比特怎样表示数
同一串比特没有天然含义。0x3f800000 可以当作无符号整数 1065353216,也可以按 IEEE 风格单精度浮点格式解释为 1.0;0xffffffff 在无符号 32 位整数里是 4294967295,在二补码有符号解释里常被读作 -1。机器执行的是比特操作,程序语言和 ISA 给这些比特加上类型、宽度、对齐和舍入规则。
本篇回答三个问题:定宽整数为什么会回绕;字节序和对齐为什么影响内存解释;浮点加法为什么不能按实数代数随意换括号。证据等级为“手算”和“功能执行”。本篇的 C 程序只执行定义良好的 unsigned 运算、memcpy 对象表示复制、对齐查询和浮点分组;不执行有符号整数溢出。
整数:宽度先于意义
以 8 位为例,1111 1111 如果按无符号数解释,就是:
1 | |
如果按二补码有符号数解释,最高位权重是 -128,其余位仍为正:
1 | |
同一比特串没有变,解释规则变了。C11 对 unsigned 运算给出明确规则:无符号运算不会溢出,超出可表示范围的结果按“最大值加一”的模数归约。WG14 的项目页把 C11 对应到 ISO/IEC 9899:2011,并列出相近草案 N1570;N1570 对 unsigned 运算的描述正是本文采用的依据。相反,C 表达式求值出现结果不在类型可表示范围内的异常条件时,行为可能是未定义;本篇不把“有符号溢出在某台机器上看起来回绕”写成 C 语言保证。
RISC-V 的整数指令又是另一层。RV32I 规范说整数运算在 XLEN 位寄存器上操作;ADDI 和 ADD 的溢出被忽略,低 XLEN 位写回目标寄存器。于是同一段二进制加法,在 C 的 signed 语义、C 的 unsigned 语义和 RV32I 指令语义中,边界并不完全一样。写文章和做实验时必须先说清楚是哪一层。
字节序:内存按字节编号
32 位字 0x01020304 如果保存在 4 个连续字节里,有两种常见顺序:
1 | |
本机 C 程序用 memcpy 复制对象表示,输出:
1 | |
这说明当前宿主机器上,这个 32 位对象的最低有效字节放在最低地址,是小端。这个输出只证明本机 C 对象表示的观察,不自动推出所有 RISC-V 平台、所有网络协议或所有文件格式都是小端。第 00 篇的教学执行器也按小端装入 RV32I 字;这是一条实验约定,后续模型都沿用它。
对齐:地址不是任意切分都等价
结构体:
1 | |
第一个字段只需要 1 字节,但第二个字段是 32 位整数。当前本机输出:
1 | |
value 放在偏移 4,整个结构体大小是 8。中间 3 个字节不是程序员写出来的字段,而是实现为了满足对齐要求插入的填充。C 标准把对象对齐作为实现属性处理;ISA 和微架构则决定非对齐访问是否允许、是否陷入、是否被拆成多次访问。本系列的基础 RV32I 执行器把非对齐 lw/sw 作为错误处理,这不是完整硬件事实,而是教学模型边界。
浮点:近似值会改变代数直觉
浮点数不是任意精度实数。ISO/IEC 60559:2020 规定浮点格式和运算方法,目标是让给定输入下的结果可一致复现;C 标准也允许浮点表达式在实现定义条件下发生收缩。对程序员来说,最早要记住的事实是:每次浮点运算都可能舍入,括号改变舍入发生的位置。
本机程序计算:
1 | |
输出:
1 | |
手算解释是:左边先把 1e20 和 -1e20 抵消,再加 3.14;右边先算 -1e20 + 3.14,3.14 相对 1e20 太小,在该量级的表示中被舍入掉,再加 1e20 得 0。这里的 3.1400000000000001 是二进制浮点不能精确表示十进制 3.14 的显示结果。
核心案例
本篇附件 representation.c 的核心输出为:
1 | |
验收结论分层如下:
| 观察 | 证据等级 | 能支持的结论 |
|---|---|---|
UINT32_MAX + 1u = 0 |
功能执行 | 本 C 程序中的 unsigned 32 位运算按模数归约 |
0x3f800000 经 memcpy 到 float 得 1.0 |
功能执行 | 本机该表示与单精度 1.0 的常见编码一致 |
host_byte_order_probe=0x04030201 |
功能执行 | 本机对象字节序观察为小端 |
| 浮点左右分组不同 | 功能执行、手算 | 浮点加法不能按实数加法直接重排 |
程序包含 sizeof(float) == sizeof(uint32_t) 的静态断言;unsigned 回绕、0x3f800000 到 float 的复制和浮点非结合性都有运行时校验,失败会非零退出。结构体大小和字段偏移是本机布局观察,不是 C 语言不变量。这些结论不包括:所有平台的 long double 格式、所有编译选项下的浮点收缩行为、硬件浮点单元内部实现、非对齐访问在商品 CPU 上的代价。
练习
练习 1:8 位 unsigned 中,250 + 10 的结果是多少?同一低 8 位按二补码 signed 解释是多少?
答案:250 + 10 = 260,按 256 取模得到 4。低 8 位是 0000 0100,按二补码 signed 解释仍是 4。
练习 2:0x00000001 在小端 32 位对象里按字节从低地址到高地址怎样排列?如果读作 32 位整数,数值是否变了?
答案:低地址到高地址是 01 00 00 00。只要读写双方同意这是同一个 32 位小端对象,数值仍是 1;如果把这 4 个字节按大端格式解释,就会得到不同数值。
参考资料
- WG14,C 标准项目状态与 C11 对应草案: https://www9.open-std.org/JTC1/SC22/WG14/www/projects.html
- C11 N1570 草案: https://open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf
- IEC,ISO/IEC 60559:2020 浮点算术标准说明: https://webstore.iec.ch/en/publication/66123
- RISC-V International,RV32I 规范: https://docs.riscv.org/reference/isa/v20260120/unpriv/rv32.html
- 本篇实验附件:representation.c、representation-output.txt、intro-environment.txt




