计算机里的 0 和 1:从一位一位排数字,到读指令让整台机器动起来
打开一台电脑,你在屏幕上看到的画面、敲进去的文字、算出来的结果,追到最底层,全是一连串只有 0 和 1 的电信号。这串 0/1 看起来单调得可怜,可它既要表示出你写下的任何一个数,又要驱动整台机器去做加法、跑程序。这篇导读要回答的,就是那句困扰过无数第一次接触计算机的人的问题:一台只会掉两个信号的机器,凭什么算得又快又全?
答案是步步升级的。我们先用"位权"给每个数在 0/1 世界里安一个有限的家;接着发现这个固定位数的家装不进正负号,于是试出符号位方案、又发现它有两个无法忍受的坑;为了让加减能共用同一套电路,补码顶着"绕圈的钟"出场;位数有限终归会溢出,我们用廉价判据抓住它;为了塞进小数和光速级巨数,float 用一段指数换回整条取值谱,而 AI 训练天天用的 bfloat16 正是从这根刻度表上长出来的;最后镜头拉到整台机器,看同一串 0/1 如何既当数又当指令,在取址、译码、执行里让程序自己跑起来。走完这条线,那个黑盒也就拆开了。
1. 为什么计算机偏要数着 0 和 1 过日子?——位权,数字的第一个家
先看一个反直觉的事实:一个数到底值多少,跟我们用什么记法几乎无关。十进制的 9472、二进制的 0b10010100000000、十六进制的 0x2500,写起来完全不同,说的却是同一个值。关键在于,这里每个数字都有一个"位子",而位子决定了这个数字值多少。
位置记数(positional notation)说的是这样一件事:每个符号,它的值不仅取决于自身长什么样,还取决于它住在哪一个位置上,而每一个位置都代表固定基数(radix)的一个幂。Positional 拿罗马数字打比方最直观:罗马数字是"加性"的,CCXXVIII 就是把 C、C、X、X、V、I、I、I 各自固定的值加起来等于 228。而阿拉伯数字 228 里同一个"2"待在百位就值 200,待在十位就值 20,位置一变身价就变,这就是位置记数的妙处。
来亲眼看一下这几个词是怎么拼出一个数的。

图注:位置记数的五个词一次对上:Number(整个数)、Position(位置)、Digits(数位)、Radix(基数,这个数是 10 进制所以基数是 10)、Index(从右往左数到第几位)。最左边那个"1"的位置说明了一切:它坐在第 3 位上,代表 10 的三次方。Positional
把这套话翻成公式。一个 n 位、基数为 B 的数,设它的各位数字从右到左是 $a_0, a_1, \dots, a_{n-1}$,那么这个数的值就是每一位数字乘上自己那一位的权(基数的幂)再加起来:
$$ \text{值} = \sum_{i=0}^{n-1} a_i \times B^{i} $$
拿课程的 9472 走一遍就懂了。它是十进制(B=10),各位是 9、4、7、2:
$$9472_{10}=9\times10^3+4\times10^2+7\times10^1+2\times10^0$$
十位上的 7 之所以值 70,不是因为它大,而是它坐在 $10^1$ 这个位置上。Positional
人类用它那十根手指发明了十进制,玛雅人用二十进制,时间的 12/60 进制也还活在你手腕上的表盘里。光是历史上出现过这么多种进制,就该明白:写数用的记号本身毫无意义,权利在于"一位值多少"。那么计算机为什么偏偏挑了只有两个符号的二进制?答案朴素到有点扫兴:电子电路里弄"亮/灭"两个能区分开的状态,比精确区分十种电压要省事可靠得多,二进制正是最容易在电路里高效实现的那一种。Positional
计算机里三位一组、四位一组给二进制人分个家就自然产生了两种速记:每 3 个二进制位可以对应一个八进制(octal)位,每 4 个二进制位正好对应一个十六进制位。所以 0–15 这十六个值,一个十六进制符号就能写完;4 位 1111 = 十六进制的 F。C 语言系代码里 0b 开头是二进制、0x 开头是十六进制,2A5D 这种东西你以后看汇编会天天碰见。Positional
一个 bit(比特)本身不代表任何含义,它只是一盏能亮能灭的灯。真正让 0/1 有价值的是编码 + 解释这套动作:n 位能排出的不同组合最多有 $B^n$ 种,于是 26 个字母至少需要 5 bit($2^5=32>26$),500 个学号需要 9 bit。所以你看,计算机记一个数,记的从来不是"值",而是"怎么把各位按 2 的幂加起来"。这本书后半段讲的所有花招,符号位也好补码也好浮点指数也好,全是在同一块"有限位数怎么装下无穷"的地基上盖起来的。
2. 只有固定的几位,怎么往里塞正负号?——试过符号位,它给了两个坑
上一章我们让一个 bit 可以表示 0 到 $2^n-1$,也就是只会数非负数,术语叫无符号整数(unsigned integer)。但对一张欠款单来说不免尴尬:欠债和收款都得用同一串正数。于是自然冒出朴素想法——把一位(最高那位)空出来当符号位:最左那位叫 MSB(Most Significant Bit,最高有效位),约定 MSB=0 表示正、MSB=1 表示负,其余位照旧按无符号展开,这就是符号-数值表示(sign & magnitude),最左那位负责"符号",剩下的一起表示"数值的大小"。P&H
可是在这份 PDF 里,同一个 4 位串在不同解释下会读出完全不同的数,只看你怎么规定。这一小段就是全书反复出现的主题:一串 0/1 本身是哑的,意义全在"解释方式"。
| 4 位串 | 按无符号读 | 按符号-数值读 | 按偏置(bias=7)读 |
|---|---|---|---|
0111 | 7 | +7(符号 0) | 0 |
1000 | 8 | −0(符号 1,幅值 0) | 1 |
1111 | 15 | −7 | 8 |
表里那张"−0"就是符号-数值第一个坑:0000 和 1000 一个是 +0 一个是 −0,两个零并存。第二个坑更致命:做加法时不能直接把两个数当无符号二进相加——得先看谁的符号大、谁的号小,再决定用加法器还是减法器去对齐,等于让加法每次都要先"判号"。第三个坑是范围被砍:用了 1 位符号,能在幅值上表示的区间就只剩一半,正负各少一个可编码的值。
想象一下让你用现金结算:如果每张欠条你要先翻看是收是付、数额谁多,还要把收的付的错开才能对上账,这套账本绝不是给机器用的。符号位方案凭这三个不能忍的坑,被现代硬件整体弃用。P&H
顺带认识第三个橱窗货:偏置表示(biased notation)。它给所有编码加一个固定的偏置值,让原本 0 开头的负数搬进可表示区。上表按 bias=7 读,0111(7−7=0)是 0,1000(8−7=1)才是 1——它在这里同样没法直接比较正负谁大谁小,可是它有个别的用处:浮点数的指数会用上它,那是第 5 章的话题,先埋个伏笔。
真正让硬件满意的方案,得满足一个铁律:加减必须能自动共用同一套电路,绝不能再让加法去额外判号。这个要求把下一章的主角——补码——推上了台。符号位不是错的,错在只借用了一位而把其余位当成了"一模一样的正数位数",补码的高明之处在于,它让每一位在加的时候都真正参与进来。
3. 补码:把一个会绕圈的钟请进硬件——从此加减共用一套电路
这一章是整本书最要紧的一座山,但一旦抓准"绕圈的钟"这个意象,跳过去平坦得惊人。
先借钟表"绕圈"这个意象把话说清。4 位的补码其实是一个十六格的盘($2^4=16$ 种编码),按模 $2^4=16$ 走圈:从 0 顺时针数到 7,再往下不是 8 而是跳回 −8、−7、…、−1,转满一圈回到 0。**模(mod)**在这个语境里就是"绕满一圈回来"的周期。你一旦接受数字会绕着这个圈走,补码的三大怪癖一次全有了答案:全 0 那格只有一个零(正上方那格),负数比正数多一个(多出个 −8 蹲在最底下没人陪它),以及"取反加一"这句口诀。
看下面这张同心数环图,三种表示法摆在一起。

图注:同样是 4 位,最外黑环是原始二进制编码;白色环按无符号 0–15;橙色环按反码、多了个 −0;青色环按补码 0、1…7、−1、−2…−8,只有单一零。中心那个顺时针的 "+4" 箭头画出了魔法的来源:在表盘上给任意一个数加 4,越过一圈的边界(比如 7 加 4)就自然"回卷"到负区那一格。Two's complement
这时候课程的 3 位罗盘图就很好用了,它只画了最省心的补码自己。

图注:3 位补码的罗盘:从正上方
000=0 顺时针走,001=1、010=2、011=3,再往下一步100不读 4,而是翻到最负的 −4(红色),然后 −3、−2、−1,最后兜回 0。转一圈刚好把 $2^3=8$ 种编码用光。
补码的数学定义很短:对一个 N 位系统,负数 x 不是用它的绝对值来存,而是存"从 $2^N$ 里减去 |x|"得到的那个补值,写法是
$$x^* = 2^N - x$$
所以 −6 在 4 位下存的是 $16-6=10$,也就是 1010。为什么大家一辈子只记"取反加一"?因为取反加一是做这件"从 $2^N$ 减去"的廉价捷径。这能用等式一眼看穿:把 x 逐位取反,得到的是全 1 的串减去 x(全 1 串减 x 恰好就是逐位取反);取反之后再加 1,就是 $(2^N-1)-x+1 = 2^N-x$,而这正是刚才那句"补码就是对 $2^N$ 的补"的定义。Two's complement
用 Python 验证时有个坑:bin(-7) 会输出带负号的 -0b111,而不是补码位串,初学者常被它骗。要写固定位宽,用位掩码把它括在 8 位里:
>>> bin((-7) & 0xFF) # 掩成 8 位,得到补码
'0b11111001'
>>> 15 + (-5) # 补码下 -5 = 0b11111011
10 # 不做任何符号判定,直接相加即得
第二行演示的是补码真正的卖点:15 + (−5) 在硬件眼里就是 0000 1111 + 1111 1011,两个无符号二进相加,丢掉的越界第 9 位进位不管,剩下的 0000 1010 因为 8 位截断而恰好等于 10。Two's complement
代价是什么呢?一是正负各占一半、且正数侧含 0,所以负数比正数多一个;二是存在那个没人陪的"怪数"——最负值 −8,它的补码还是它自己(取反 0111…+1 又绕回 1000…)。这两笔"账"换来的是加减共用一套电路、无符号与有符号同一副加法器,这个权衡赢得了今天所有现代硬件。P&H
所以,负数在补码里不是外加了一个"负号"标记,而是被存成一个"能直接和无符号数一起进加法器"的数。符号位方案被推翻不是因为符号位这个念头错,而是它造出的那个数没法直接参与加法;补码让最高位既当符号又在加权——它带着一个负的 $2^{N-1}$ 权重。这个让最高位"又当符号又在算"的回旋,正是补码配得上所有现代硬件的原因。
4. 算出来的数字跑出了那固定的几位——补码也会溢出,怎么提前抓住它
数字在数学里是无封顶的,硬件却只有固定几位。前几章我们默认"放得下",可一旦算术结果超出这几位能表示的区间,就出**溢出(overflow)**了:结果在数学上并不错,只是装不进这个有限位数的抽屉。
我们亲手撞一次。4 位补码下做一个看似无害的加法:
0111 (+7)
+ 0001 (+1)
--------
1000 → 若按补码读 = -8
+7 加 +1 数学上是 +8,可结果 1000 在补码里读作 −8。数字一步跨过了表盘上 7 和 −8 那道接缝,从最大正数"翻车"成最负负数。无符号那边同样是加 1 却安然无恙:1111 + 0001 回绕成 0000(当作 16 溢出丢掉了),在无符号下那是正常的进位回绕。看下面的无符号数轴图,那条把最右兜回最左的蓝弧,就是"加到进位就绕回 0"的这一路口味。

图注:4 位无符号数轴
0000→0001→0010…→1110→1111,蓝色弧形箭头把最右的1111兜回最左的0000,示意这一路加法到顶就自然回绕。标签里的 "0b" 是二进制前缀。
可补码那一路就没这么幸运:它同样会绕圈,但绕的位置在正负分界线上,所以一绕就绕成了负数。要提前抓住它,有个既直观又可考格的判据:溢出发生在两个正数相加得出负数、或两个负数相加得出正数的时候,正常一正一负相加永远不会溢出(结果一定落在范围内)。原因很直觉:同号相加要把结果送进对方区,只要出去了就变号了。Two's complement
做加法器的人还给这条判据配了个电路版速判:观察进到最高位(MSB)的那一位进位和从 MSB 出来的那一位进位是否一致——若两者是 01 或 10(一个进了没出来、或出来了没进去)就有符号溢出,一致的 00 或 11 才正确。这条"carry-in 与 carry-out 不一致即溢出"常常直接考,XOR 一下两个进位就能说出来。
硬件面对溢出不打算帮倒忙, RISC-V 的官方口径说得很坦白:加法指令的溢出被忽略,只有结果的低 XLEN 位被写回寄存器,不产生任何算术异常。RISC-V 换句话说,溢出不是电路崩溃,而是被设计成"丢弃高位的既定行为"——所以它很少报错,只会悄悄以错误的符号或数值从寄存器里爬出来,等你在一句 if 里发现结果不对早已晚了。这解释了为什么"安静出错"是最难找到的 bug 形态之一。
所以固定的几位要么表达得了、要么表达不了,这不由你说了算。而值得庆幸的是,会不会溢出有廉价的判据能提前认出来,最坏情况也只是"按规格丢进位",而不是让整台机器罢工。理解了这个边界,我们才敢放心地去给位数有限这件事做第二次手术——这回连小数和天文数字都要塞进去。
5. 小数点、光速级巨数也想装进 32 位——IEEE 754 用一段指数换了整条取值谱
整数这条路走到尽头了。用固定位数的整数格子,既装不下 0.000001 这种贴地的小数(格子太粗),也装不下 $1\times10^{38}$ 这种天文数字(格子太少又太挤)。为什么?因为整数给每一位都配了一个固定的权,间隔是硬编码的,你没法靠挪动几位就同时覆盖悬殊好多个数量级的范围。
浮点(floating point)的想法是:与其老老实实把每一位当整数权重,不如学着科学计数法,把"值有多大"(指数)和"是哪一段里的 x.xxx"(小数部分)拆开存。有限位数必然只能逼近而无法精确表示大多数实数,因此一套标准必须规定确定的舍入方式,否则两台机器算同一个式子会给出不同的答案——这正是 Goldberg 三十年前那篇浮点圣经的核心警告,也是 IEEE 754 标准要锁死的东西。Goldberg
现行版本是 IEEE 754-2019,它对单精度(binary32 / 32 bit)这样分三段。IEEE 754-2019

图注:单精度 binary32 的三段布局:S(1 bit 符号位)、E(8 bit 指数,用 +127 偏置记)、F(23 bit 尾数,约定丢掉的隐含最高位 "MSB dropped")。宽度大致按 1:8:23。P&H
三段的含义逐条说。符号 S 不用多讲。指数用的是第 2 章打过埋伏的偏置记法:真实指数存成"真实指数 + 偏置 127",这样就能把负指数也塞进无符号排序的 8 位里,偏置记法在这里终于派上用场。尾数 F 有个妙招叫隐含位:因为规范化的二进制 $1.xxx\times2^e$ 最高位一定是 1,所以干脆不存那个 1,省出 1 bit 给精度,这就是图上 "MSB dropped" 的意思。
拿课程里的光速一步步编一遍,别无脑跳。设 c ≈ 0.2998×10⁹ 米每秒。
- 转成二进制、再写成科学计数法 $1.0001\dots\times2^{28}$;
- 符号 S=0(正数);
- 指数偏置:E = 28 + 127 = 155 =
1001 1011; - 尾数:丢掉科学计数法里的隐含前导 1,只留它后面 23 bit。
于是 32 位串的符号与指数段拼出来就是 0 | 10011011 | F:其中 E=28+127=155 即二进制 1001 1011,而 23 位尾数 F 是按 IEEE 754 就近舍入(round-to-nearest)把丢掉的隐含 1 之后那段二进制小数截出来的,P&H 具体那 23 位该写什么、末位取 0 还是 1,取决于所用的舍入规则,直接照抄某一个位串容易抄错——实务上交给二进制转码器去算最稳。
现在把镜头交给你是 AI 新生最该认识的那个格式。bfloat16(Brain Floating Point)只有 16 bit,布局是 1 bit 符号 + 8 bit 指数 + 7 bit 尾数。BFLOAT16

图注:bfloat16 的 S / E(8, 偏置 +127) / F(7) 布局,跟上一张 binary32 唯一不同是把尾数从 23 位削到 7 位,指数仍是同样的 8 位、同样的 +127 偏置。BFLOAT16
同一个光速 c,指数照旧是 1001 1011,尾数则从 23 位砍到只剩 7 位:只保留 FP32 那 23 位尾数的前 7 位时,被砍掉的第 8 位之后该不该再进一位(就近舍入)还是直接丢掉(向零截断),会让末位在 0 和 1 之间取舍——这就是课程里那行"末位一上一下"的由来。所以 bfloat16 对光速这颗"在误差容忍区"的数并不比 FP32 糟多少——它的短板在日常小数的精确位上。
但你也务必记住一个容易写错的口径:bfloat16 并不是 IEEE 754 的成员格式,它是科研与产业里参照 binary32 布列出来的一种非标准布局。Intel 那篇 2019 年的论文讲清了它为什么为 AI 而生:它的指数范围跟 FP32 一样宽,于是深度学习训练时不需要像用 IEEE 半精度 FP16 那样,去折腾复杂的 loss-scaling 调参,就能直接处理很小很小的梯度值。BFLOAT16
看着下面这张混精度数据流图,你会看到 bfloat16 不只是课本一个格式,而是训练流水线里真实的角色。

图注:深度学习里的混精度训练数据流:核心 GEMM 矩阵乘接收 BFLOAT16 张量输入、以 FP32 累加器相加,标 Q(Quantlib 模拟器)的环节又把 FP32 结果改回 BFLOAT16 传给下一层,权重与偏置更新则一直留在 FP32 保精度。BFLOAT16
总结这一幕:浮点是把整数的位权表换了行——少留几位给整数区间,多留几位指数去铺数轴两端那巨大的范围,换来"能用一个固定格式去逼近连续实数"。而 AI 训练,正掐着同一根刻度表、把尾数削短到 7 位去做低精度,这不是课本冷知识,是你训练模型时天天在用的底层事实。BFLOAT16
至此,一串 0/1 怎么当数字算,我们讲完了。现在把镜头拉远,问一个更"整机"的问题。
6. 这些 0 和 1 住在哪儿,怎么被机器读着跑?——存储程序与一次完整的取指执行
先抛一个时间错位:1946 年宾夕法尼亚大学的 ENIAC 算起 10 位乘 10 位只要 2.8 毫秒,快得惊人;可要换一个程序,工程师得靠插拔成排的插线(patch cords)和拨开关,动辄两三天。EDVAC
运算能力和改程序的速度完全不成比例,这是痛点。
冯·诺依曼 1945 年 6 月 30 日分发的《First Draft of a Report on the EDVAC》,第一次公开把这个想法写成文档:把整个程序像数据一样存进同一个内存,换程序只需改内存的内容,不必重新连线。报告里那句关键表述是"控制部件收到的指令来自内存,也就是存放数据材料的同一个地方"。EDVAC
这个公认的第一署名带有长年争议——宾州大学 Moore School 的 Mauchly 与 Eckert 主张概念源自更早的内部讨论会,所以教程的口径都应温和,把功劳说成"报告首次公开写成",不独归于冯·诺依曼一人。EDVAC
"程序像数据一样存在内存里"这句话最要紧的推论,看下图就懂了:内存是一条地址到内容的清单,上半格是"指令",下半格是"数据",它们住在同一张表里,仅仅分隔在不同的地址区间。

图注:内存是一张"地址 → 内容"的清单:蓝色区存指令(如地址 0x00 的内容 0xde),绿色区存数据(0x03 起是 0xef…);指令与数据共用同一套地址寻址,只是放在表格不同段落。
那么问题来了,同一段内容,机器怎么知道该把地址 0x00 里的 0xde 当成"命令"还是"数值"?答案朴素到反直觉:取决于解释。课程给过一个经典例子:十进制数 7,537,331 写成十六进制是 0x007302B3,把它当一个普通的数读是一个值;可如果你按 RISC-V 指令的编码规则去读,它就是一条 add x5, x6, x7(把寄存器 x6 和 x7 加起来放进 x5)。同一个 32 位串,当数还是当命令,全看用哪套"解释字典"。
这套解释规则叫 ISA(指令集架构),是硬件厂商给"一串 0/1 该干什么"定的官方字典。RISC-V 就是其中一种,也是这门课用的范例。把一条 add x5,x6,x7 编码成 32 位时,你会看到它其实是一段按六个字段切开的长条。

图注:一条
add x5, x6, x7被切成 6 段 32 位机器字:funct7(0x0) |rs2(x7→0x7) |rs1(x6→0x6) |funct3(0x0) |rd(x5→0x5) |opcode(0x33)。箭头把汇编助记符与三个寄存器号一一指向它们对应的字段位。
讲清楚字段,才算真正摸到指令。RISC-V 的 R 型指令格式从高位到低位依次是:funct7(7 位)| rs2(5 位)| rs1(5 位)| funct3(3 位)| rd(5 位)| opcode(7 位)。其中 opcode 告诉机器"这是一条运算指令",funct7 和 funct3 一起具体选"做什么运算";add 对应的 opcode 是 0110011(十六进制 0x33),funct7 和 funct3 都是 0;而 rs1、rs2 是两个源寄存器号,rd 是结果写回的目标寄存器号——RISC-V 有 32 个通用寄存器 x0–x31,所以每个寄存器号只要 5 位就够了。RISC-V
看到这你该有个恍然:第 3、4 章费了半天劲讲的补码,这时派上用场了——rd 里那个被读出来的 x6、x7 的值,是按补码解释的有符号整数,加出来的进位回绕(溢出丢高位)也正是 RISC-V 上章的官方行为。RISC-V。前几章的位表示和这一章的指令世界,在这里接了缝。
有了指令,处理器怎么让它动起来?处理器内部有两大块分工:**数据通路(datapath)**负责真正干活的部件——寄存器、算术逻辑单元、存储器访问单元等等;**控制(control)**负责决定每一步该干什么、什么时候干。用生产线的比方:数据通路是产线上的机床、机器人、传送带,真正完成加工;控制像生产计划与质量管控,按计划调度各设备。P&H
下面这张图把一次执行画成了带箭头的回路。

图注:一次取指-执行的路径:PC(程序计数器,指着下一条指令的地址)→ Memory 取回指令 → Decoder 译码 → Registers 读出源操作数 → Function Units 做运算 → 结果写回寄存器/内存。Processor 是上层模块,下面统一连着一整块 Memory。
把一条 add x5,x6,x7 走完整条流水,一共四步:取指(Fetch),PC 把地址发给内存;译码(Decode),内存返回的指令送入译码器,译码器认出 opcode 与另外两个源寄存器;执行(Execute),功能单元从寄存器 x6、x7 读操作数(按补码解释,回扣前两章!)做加法;写回(Writeback),把结果存进目标寄存器 x5。这四步一拍一个,机器就自己把一条指令"搬"起来了。RISC-V
讲完了"指令在处理器里怎么跑",最后一个问题交给存储:这些 0/1 程序和数据,在物理上究竟住在哪一层?
如果按"越快越贵"和"越大越慢"两个坐标,存储器从寄存器一路排到磁盘像一座金字塔。课程那张金字塔图,自顶向下是寄存器、L1/L2 缓存(SRAM)、DRAM 主存、本地磁盘、远程存储;越往尖上越小越快越贵且还要通电保持,越往塔底越大越慢越便宜越能断电保存。Computer memory
内存(这里的"主存/内存"特指半导体 RAM 那层)又有易失性这把大剪子:易失侧的 RAM 靠供电、断电即丢数据,所以主存 RAM 又细分成易失的 SRAM(静态,用于 CPU 缓存)与 DRAM(动态,用于主存主力);而同属半导体却断电不丢的 ROM 家族(PROM/EPROM/EEPROM…)则是另一条非易失侧,并不像 RAM 那样分成 SRAM/DRAM。这道 RAM 易失 / ROM 非易失的分界,正是你主板上的代码在开机重启后还能保住的原因。Computer memory
那磁盘(硬盘/SSD)呢?它有个非常容易被当成又一道"内存"的位置,但结构上它不是。英文里 memory 通常特指 CPU 能直接字节寻址、随机访问的那片半导体;而磁盘走的是 I/O(输入输出)通道、按较大的块搬进搬出、CPU 不能逐字节直接寻址。你调一块磁盘数据,先要把它整体搬进 RAM 才能用——所以课程把磁盘归在 I/O/辅存侧而非内存,这个区分是考点,也是理解 memory hierarchy 的关键一跃。Computer memory
所以,同一个 0/1 串在整台机器里的"住址",从寄存器到 Cache 到内存到磁盘,是按快慢贵贱分层住的;磁盘在结构上属于 I/O。此刻,你手上那个黑盒已经被拆成了三层能对上的故事:一串 0/1 怎么做成数字(第 1–5 章),怎么被 ISA 解释成一条命令(第 6 章取指这步),命令怎么在按快慢分层的存储之间把数据搬来搬去、由处理器读着执行——CPU 今天能跑起一部模型,靠的正是这根从十进制一路长到 bfloat16、再喂进 GEMM 累加器的完整链路。
参考文献
本专题引用的核心资料如下,建议深入学习时阅读原文:
- Computer Organization and Design: The Hardware/Software Interface, RISC-V Edition (2nd ed.),Patterson & Hennessy — Elsevier 出版页
- Positional notation — Wikipedia 词条:https://en.wikipedia.org/wiki/Positional_notation
- Two's complement — Wikipedia 词条:https://en.wikipedia.org/wiki/Two%27s_complement
- What Every Computer Scientist Should Know About Floating-Point Arithmetic,Goldberg (ACM Computing Surveys, 1991) — Oracle 重印
- IEEE 754-2019 Standard for Floating-Point Arithmetic — IEEE SA 官方页
- A Study of BFLOAT16 for Deep Learning Training,Kalamkar 等 (2019) — arXiv:1905.12322
- The RISC-V Instruction Set Manual, Volume I: Unprivileged Architecture (RV32I) — docs.riscv.org
- First Draft of a Report on the EDVAC,von Neumann (1945) — Wikipedia 语境页
- Computer memory — Wikipedia 词条:https://en.wikipedia.org/wiki/Computer_memory