标签: 计算机科学基础

  • 0 和 1 为什么能表示整个数字世界

    作者:lihao技术文章发布:计算机科学基础 · 第 02 期

    从四位数、UTF-8文字和灰度像素出发,说明有限状态怎样通过编码组合成数字媒体,以及位串为什么不能自己说明含义。

    两种状态很少,组合却可以很多

    一个开关只有开和关;四个独立开关却有16种组合。计算机把两种可区分状态抽象为0和1,称为一个比特(bit)。n个比特有2的n次方种位串:这是组合数量,不是每个位置都藏着丰富内容。

    物理上不必是“完全没电”和“有电”。电路使用允许范围内可区分的电平等状态,并处理噪声、时序和可靠性。二进制是一种适合构造稳定数字逻辑的表示,不意味着现实世界本来只有两种事物。

    要让位串表示数字、文字或图像,需要编码规则。表示能力来自组合和约定;理解含义则还依赖格式、上下文与解释程序。

    先让位串表示一个整数

    约定四位无符号二进制从左到右权重为8、4、2、1,那么1101表示8+4+0+1=13。全部四位位串可以表示0到15,总共16个值。若想表示16,必须扩展位数,或更换编码。

    同样的1111,在四位无符号解释下是15,在四位二进制补码解释下是-1。这不是电路同时看见了两个数,而是解释规则不同。位宽、符号规则与字节顺序等,都是数据格式的一部分。

    因此,“计算机里全是0和1,所以类型不重要”恰好说反了:正因为底层位串可以有多种解释,类型和协议才帮助我们排除错误解释。这里的补码只是反例,负数的完整运算规则留待后续。

    文字不是把字形直接塞进一个数

    Unicode为字符分配码点;UTF-8再把码点编码为一个或多个字节。字符A的码点为U+0041,UTF-8字节是十六进制41;“中”为U+4E2D,UTF-8字节是E4 B8 AD。[1] 十六进制只是紧凑的记数方式,每一位十六进制数对应四个比特。

    这个字节序列可以手算。U+4E2D 写成 16 位二进制是 0100 1110 0010 1101;它处于 UTF-8 的三字节编码范围,采用 1110xxxx 10xxxxxx 10xxxxxx 的形式。把码点有效位分成 0100、111000、101101,填入三个位置,得到 11100100、10111000、10101101,也就是 E4 B8 AD。[4] 这里示范的是 U+4E2D,不能省略 UTF-8 对代理项、范围和最短形式的合法性约束,再把所有 16 位整数都当字符编码。

    一字节通常为8位,但“一个字等于一个字节”不成立。字符、码点、编码单元以及屏幕上看到的字形并非一一对应;组合附加符号和表情序列尤其能显示这种区别。

    收到E4 B8 AD之后,软件还需要知道按UTF-8解码,再由字体和排版系统绘制字形。若拿错误编码读取,位串没变,屏幕却可能乱码。Unicode的编码规则支持信息交换,不替我们决定一句话是否正确或有意义。

    图像和声音怎样进入同一套表示

    构造一张2×2的8位灰度图,约定按行保存四个像素,数值为0、85、170、255。像素负载是4×8=32比特,即4字节;0最暗、255最亮。真实文件还要描述尺寸、排列、颜色空间等,不能把4字节负载当作完整文件大小。

    照片先经过空间采样与数值量化,保留有限个位置与有限精度。声音也可以在一系列时刻记录量化后的振幅。下面只作未压缩双声道PCM的负载算例:每秒48000次采样、每个声道每次16位,负载为48000×16×2=1536000比特/秒,即192000字节/秒。

    这个算例不含容器头、元数据与压缩,也不保证采样后的声音与原始连续信号完全相同。编码能保存我们选择保留的信息,不能让有限数据无损覆盖任意连续细节。

    压缩和加密改变的是什么

    如果图像有大量重复或可预测结构,无损压缩可能用更少的位保存它;解压后恢复原位串。有损压缩允许丢掉某些信息,以换取更小体积。加密则依据密钥改变可读取的形式,目标也不同。

    不可能让所有固定长度位串都无损压得更短:短位串的总数量不足以给每个输入分配不同输出。压缩方法利用输入的结构和分布,不会推翻这个计数事实。Shannon在1948年的论文中区分了通信工程问题与消息的语义问题。[2] 这一区分有助于避免把“信息量”直接等同于思想深度。

    所以,0和1能共同承载数字媒体,并不表示所有东西都能被有限、精确地表示,也不表示机器理解了内容。真正可用的数字世界建立在位串之上的多层契约:编码、格式、程序、设备和使用语境。

    一张像素图,需要的不只是四个亮度值

    继续使用0、85、170、255四个字节。若约定宽2、高2、逐行存储,就形成左上最暗、右下最亮的2×2灰度图。若约定宽4、高1,同样负载变成一行;若把相同字节解释为颜色通道,又会得到别的对象。

    因此,一个可交换的格式至少要交代尺寸、通道、数值精度、顺序与解释方法。像素数据像一串没有标点的数字,格式负责说明如何分组。头部或外部协议并非无用开销,而是让另一端能恢复对象的必要条件。

    还要区分像素值与显示亮度。8位灰度数值只是编码,显示器的响应、色彩管理和观察环境会影响看到的光。数值128也不必自动等于人眼感觉上的“一半亮”。本例只说明编码容量,不用数值表冒充视觉质量实验。

    如果把每个像素从8位减为1位,每个位置只剩两种取值,四个位置总负载降到4比特;原来的85和170就无法原样保留。体积变小来自表示空间变小,必须决定如何近似,而不是认为二进制可以无代价保留所有细节。

    声音的采样率和位深,分别限制什么

    采样率描述每秒取多少个样本,位深描述每个样本用多少位表示。增加采样率主要改变时间轴上的离散程度;增加位深主要改变幅值量化的等级。它们是不同维度,不能用其中一个数字代替全部音质。

    前面的48kHz、16位、双声道PCM,每秒有96000个声道样本,每个2字节,因此是192000字节/秒。持续60秒,原始负载为11520000字节;按十进制MB约11.52MB,按2的20次方字节为1MiB计算,约10.99MiB。文件容器与元数据还要另外计算。

    这是容量推导,不是“某音乐文件一定这么大”。压缩编码可以改变文件大小,静音也可能被不同方式表示;编码器的质量、失真目标和音源本身都相关。把码率、采样率和响度混为一谈,会误解每个数字能够支持的判断。

    一个连续变化的物理波形,只有在适当信号条件、采样过程与重建条件下,才能讨论如何由离散样本恢复。这里不展开采样定理的证明,但必须保留条件;不能仅凭“比特足够多”宣称任何声音都能被有限数据精确保存。

    比特翻转、压缩和意义,不能混用同一标准

    若00000001中的最高位因错误变成1,按8位无符号数读取就成129;若它处在文件头,影响又可能大得多。相同数量的比特错误,对不同格式的后果并不相同。可靠传输需要检错或纠错等机制,编码能表示内容并不保证传输没有损坏。

    再看压缩的计数限制:一共有256种8位输入,而长度至多7位的位串总共只有1+2+4+…+128=255种,其中还计入空串。若要求每个8位输入都能无损恢复,就无法把它们全部映射成互不相同的更短串。

    现实压缩能工作,是因为某些输入更常见,或输入具有可利用的重复结构;方法可以让常见内容缩短,同时允许别的内容不缩短甚至变长。它与“任何文件都能反复压小”不是一回事。

    最后,65是一个数,A是按规则读出的字符,某段文字是否有意义又是另一层。信息论中的量化对象、编码后的对象和人理解的内容,应在各自问题中使用。位串是共同的承载形式,语义来自更完整的解释关系。

    一个屏幕上的“字”,为什么不一定是一个码点

    字节计数、码点计数和用户看到的字符数,可能不同。例如某些带附加符号的文字既可用预组合码点表示,也可用基础字符加组合符号表示。视觉上相近,不保证底层码点序列逐字相同。

    Unicode文本分割规范用扩展字素簇等规则处理用户可感知的文本边界。[3] 这不等于每一种语言和应用都只需一个字符计数函数;光标移动、字符串长度、截断与存储限制可能采用不同单位,必须明确接口。

    如果把UTF-8字节数组截在一个多字节序列中间,可能得到非法编码;即使保留完整码点,也可能截断一个组合序列。正确解码、正确分割与正确显示是相连但不同的工作。

    对程序接口,这些区别会直接影响字段长度与校验。要求“最多10个字符”应先解释10个字节、10个码点还是10个字素簇。把含糊需求变成具体编码契约,才让“0和1可以表示文字”成为可靠的数据交换机制。

    自己检验一次解释规则

    把01000001按8位无符号数读,是65;按ASCII兼容的UTF-8字节读,是A;按单通道像素强度读,是一个灰度值。同一串比特没有自己携带唯一用途。

    阅读文件或接口时,先问“位宽多少、用什么编码、怎样划分边界、由谁解释”,通常比只问“里面是不是二进制”更有帮助。本篇算例是编码与计数推导,不是图像质量、音频质量或电路可靠性的实测。

    来源、证据与阅读路线

    1. Unicode FAQ:UTF-8、UTF-16、UTF-32与编码单位;U+0041、U+4E2D为本文编码例子
    2. C. E. Shannon,A Mathematical Theory of Communication,1948,Introduction;大学托管原论文
    3. Unicode Standard Annex #29:Unicode Text Segmentation,扩展字素簇;浮动规范以文中概念说明为限
    4. Unicode Standard 18.0.0,Chapter 3,3.9 Unicode Encoding Forms;Table 3-6 与 3-7 的 UTF-8 位分布及合法序列

    证据范围:字符编码、像素与PCM容量、有限串计数按明确假设核对;这些算例不等于显示设备、音频链路或压缩实现的实测。

    版本1.2 · 2026-10-18:完成事实、条件与引用复核,逐篇明确证据方法;有实质缺口处补齐推导、反例或原题。原网址与首次发布日期保留。

  • 计算机到底是什么?从信息表示、计算到输入输出

    作者:lihao技术文章发布:计算机科学基础 · 第 01 期

    用一个三条指令的教学模型,连接数据编码、状态变化与输入输出;说明程序可运行和系统可用之间还差哪些条件。

    计算机为什么不只是算得快的机器

    一台机器能做加法,还不足以说明它为什么能编辑文字、播放声音或浏览网页。关键是:不同对象能按规则表示为数据,同一套可编程机制能依据指令变换数据,再通过输入输出连接外部环境。

    本文讨论数字电子计算机。它通常有处理器、存储器和输入输出设备;程序规定要执行的操作,存储状态保存当前的数据与进度。计算机不是直接认识一张照片或一句话,而是在具体编码、软件与设备协议下处理表示。模拟计算机、量子计算等不在本篇模型范围内。

    Nand2Tetris 的计算机体系结构课程把CPU、内存与映射到地址空间的设备组合起来,是观察这种分工的教学入口。[1] 这里另构造一个更小的模型,避免先被具体指令集淹没。

    把“输入一个数,输出它加一”拆开

    约定输入只允许0到254之间的整数,数据寄存器R是8位无符号数,程序只有三条指令:IN R;ADD R,1;OUT R。另有程序计数器PC指出下一条指令,开始为0。

    输入41时,第一步把外部输入解释为整数41并放进R,PC变1;第二步计算42并写回R,PC变2;第三步把R的值送到输出端,PC变3并停止。状态可以写成“PC、R、输出记录”的组合。指令不是结果本身,而是规定状态怎样变化的规则。

    若输入是字符“41”,输入层还需要先把两个字符解析为整数;若输出设备是屏幕,还要把42转成字符编码并绘制字形。我们为教学把这些工作藏进IN和OUT,因此不能用这个模型声称真实机器一条指令就完成了键盘或屏幕操作。

    一段可以亲手核对的状态轨迹

    本例的抽象轨迹如下,横线表示尚未写入。它是教学模型的手工推演,不是某种CPU的指令跟踪:

    执行后 PC R 输出
    初始 0 — 空
    IN 1 41 空
    ADD 2 42 空
    OUT 3 42 42

    读这个表应能回答:数据是什么时候进入的,哪一步改变了数值,哪一步产生了外部可见结果。如果计算完成却没有执行OUT,内部状态已有42,用户仍可能看不到输出。

    模型还必须规定错误。我们拒绝255,是因为“加一仍可表示”属于本例输入契约;另一种机器可以选择模256回绕,得到0,但那是另一套语义。不能一边说得到数学整数的加一,一边悄悄按固定位宽截断。

    程序怎样让同一台机器做不同的事

    把ADD换成别的操作,或加入条件跳转,就能改变执行路径;重复读取、比较与写入,可以组成复杂流程。程序把任务组织成步骤,处理器实现这些步骤的基本机制。程序和数据在许多现代系统中都保存在可寻址存储器里,但取指、执行、缓存与权限有各自规则。

    实际CPU会有流水、乱序执行、多级缓存,设备也可能通过DMA搬运数据。上表按顺序描述可观察语义,不声称每条指令恰好占一个时钟周期。顺序模型有助于解释“算什么”,还不足以预测“多久算完”。

    更换程序能扩大任务范围,也不能消除资源与可计算性的限制。有限内存装不下任意大的输入;某些任务可能需要不可接受的时间;也不是每个有意义的问题都能由通用算法决定。

    给模型加一次分支,程序就能作选择

    前面的加一程序只有固定路径。现在构造另一个任务:读入一个0到255之间的整数,若等于0就输出0,否则输出1。为此增加一条条件跳转:R等于0时,把PC改到“输出0”的位置;否则继续到“输出1”的位置。

    这时PC不再只是每次加一。输入0与输入41,会让同一份程序走过不同指令;选择由寄存器中的数据与跳转规则共同决定。这足以说明条件执行的基本机制,不需要先把“判断”解释成人的理解或意志。

    再加入向前面位置跳转,就能重复一段操作。例如不断读取值并累计,遇到终止标记后输出总和。循环让有限长度的程序描述任意多次重复,但每次运行仍受输入、时间和存储限制。一个没有正确终止条件的循环,也可能一直无法产生用户等待的输出。

    这里有三个需要分别检查的对象:程序文本是否包含正确分支,运行时数据使哪条分支被选择,以及输出是否满足任务。只看源代码里出现“if”,并不知道本次运行发生了什么。后续读控制流图时,正是在更明确地记录这些可能路径。

    存储器里的地址,与现实世界的对象

    把存储器先看成一排编号盒子。地址用于找到某个位置,内容是该位置保存的位串。地址8与其中保存的数8可以碰巧一样,但含义不同;修改盒子里的内容,并不自动改变盒子的编号。

    程序可以用多个位置表示一个对象,例如字符数组或一张图。对象边界由格式和程序决定,不是每个地址天然对应一个完整意义。读取四个字节究竟得到一个整数、四个字符还是四个像素,还要看约定。

    在这个简化层次里,程序读写地址,CPU执行运算;实际机器还要处理缓存、虚拟地址和权限。虚拟地址不是承诺某个固定物理位置,缓存中的副本也可能让观察更复杂。但这些机制并没有取消“必须说明读写哪个对象”的责任。

    一个有帮助的区分是存储容量与运算能力。更大的内存可以保存更多状态,却不直接让每次加法更快;更高的运算吞吐也不保证输入能及时送达。计算是状态变换,存储与输入输出决定这些状态怎样获得、保存和离开。

    从用户按键到屏幕结果,谁负责哪一步

    以键入“41”并显示“42”为例,可以列出五个层次:键盘事件被接收;输入程序得到字符序列;解析器把十进制文本转换为整数;算术过程加一;输出程序再把整数格式化为字符并交给显示系统。

    如果输入是“4l”,末尾是字母l,解析器应拒绝、提示还是截断?如果只接受整数,输入“41.5”又怎样处理?这些选择不是ALU能替用户决定的。算术单元忠实执行它收到的位模式,系统仍可能因为接口处理错误而失败。

    程序计数器、寄存器与内存因此提供了机制解释;用户任务还需要数据契约。一个结果可见但错误,和一个结果正确却没有送达,属于不同失败。定位问题时,沿数据经过的阶段逐步检查,比把一切归结为“电脑没算对”更有效。

    这也解释了为什么“计算机是输入、处理、输出”虽不错,却还不完整:我们必须进一步说明表示规则、可保存的状态、控制流程与每个边界的责任,才能理解同一设备怎样承载不同软件。

    抽象机器怎样帮助我们判断两种实现是否等价

    把机器状态记成s=(PC,R,M,I,O),分别代表控制位置、寄存器、内存、剩余输入与已产生输出。每条指令定义一项状态转换;一段程序的运行,就是从初始状态出发形成的转换序列。这个表示比“它算了一下”更明确,因为可以指出哪一项状态发生变化。

    两种实现内部轨迹可以完全不同,却在规定输入上产生相同可观察输出。例如一台机器用专用加法器,一台分两次较小运算得到结果。若接口只承诺最终值,内部差异可能不影响功能等价;若接口还承诺每个时刻的输出与响应时间,就需要更强关系。

    因此等价总要说“相对于哪些观察”。忽略耗时的数学函数模型不能证明实时响应,忽略异常的模型不能证明失败行为一致。抽象有价值,是因为它主动舍弃一些细节以回答一个问题;当问题变了,就要检查被舍弃的细节是否重新重要。

    本例的顺序状态机可以解释指令与数据怎样协作,但没有多线程、缓存一致性和设备并发。它为后续系统学习建立语言,不能靠一个小模型承诺现代计算机所有行为都如此简单。

    输入输出决定计算怎样成为一个系统

    传感器把温度等物理量转换成电信号和数字读数;网卡按协议收发数据;显示器把像素数据转成光。每个环节都可能有延迟、精度、失败和权限限制。程序算出一个数,与设备真正执行对应动作是不同事件。

    所以,“程序没有报错”并不等于结果满足用户需要。输入是否按约定解析、溢出是否被处理、输出是否送达,都是完整行为的一部分。若输入设备把41读成14,处理器忠实完成加一,系统仍然给错了用户想要的答案。

    这也是本篇的边界:它解释表示、状态变换与外部行为之间的关系,不提供真实硬件性能结论。下一篇继续追问:这些数、文字和图像,为什么可以共同建立在0与1之上?

    来源、证据与阅读路线

    1. Nand2Tetris,The Elements of Computing Systems,计算机体系结构与完整软硬件教学项目
    2. Unicode FAQ:UTF-8等编码形式,用于区分字符、编码和底层数据

    证据范围:抽象机器的状态轨迹和8位整数边界已按所定规则核对;这是教学模型,不是对真实处理器、操作系统或通用计算能力的实验验证。

    版本1.2 · 2026-10-18:完成事实、条件与引用复核,逐篇明确证据方法;有实质缺口处补齐推导、反例或原题。原网址与首次发布日期保留。