try ai
科普
编辑
分享
反馈
  • 二进制数系统

二进制数系统

SciencePedia玻尔百科
核心要点
  • 二进制系统的位置表示法允许通过简单高效的移位操作来执行乘法和除法等复杂算术。
  • 补码是一种在有限系统中表示有符号数的巧妙方案,在该系统中,溢出是模运算自然且可预测的结果。
  • 格雷码中,连续的数值仅有一位不同,这对于防止机械传感器和异步数字电路中的数据损坏至关重要。
  • 位打包是一项强大的技术,它将多个状态或受限数据表示在单个整数中,是高效软件和数据压缩的基础。

引言

从您口袋里的智能手机到模拟宇宙事件的超级计算机,整个数字世界都建立在一个看似简单的基础上:开(ON)与关(OFF)的二进制区别,由1和0表示。但是,这种基本的比特语言是如何产生如此巨大的计算能力和复杂性的呢?这个根本问题常常体现了使用技术与真正理解其工作原理之间的知识鸿沟。本文旨在通过深入探讨数字逻辑的核心——二进制数系统,来弥合这一鸿沟。

在接下来的章节中,您将揭示那些将简单位模式转变为强大的计算和信息存储系统的优雅数学和巧妙工程。第一章“原理与机制”将揭示位置表示法、用于有符号数的巧妙补码系统以及格雷码等特殊变体等概念的神秘面纱。随后,“应用与跨学科联系”将展示这些原理在现实世界中的应用,从紧凑的数据表示和高效算法到可靠硬件的设计。我们将从探索赋予0和1非凡力量的基本规则开始。

原理与机制

数字世界的核心惊人地简单。它建立在一个单一、决定性的概念之上:一个开关可以处于“开”(ON)或“关”(OFF)的状态。我们用符号111和000来标记这两种状态。但是,我们如何从这个微不足道的二进制选择,发展到能够模拟星系、创作音乐和连接数十亿人的现代计算机的惊人复杂性?其魔力不在于比特本身,而在于我们如何排列它们以及这些排列意味着什么。这就是二进制数系统的故事。

不仅仅是0和1:位置的力量

我们都熟悉十进制系统。当我们写下像354354354这样的数字时,我们直观地理解它不是“三、五和四”的简单相加,而是“三百加五十再加四”。每个数字的位置赋予了它权重。在数学上,我们将其写为 3×102+5×101+4×1003 \times 10^2 + 5 \times 10^1 + 4 \times 10^03×102+5×101+4×100。每个位置的值都是底数的幂,这里底数是10。

二进制系统是完全相同的理念,只是基数为222。一个像 (1101)2(1101)_2(1101)2​ 这样的二进制数就意味着 1×23+1×22+0×21+1×201 \times 2^3 + 1 \times 2^2 + 0 \times 2^1 + 1 \times 2^01×23+1×22+0×21+1×20,它等于 8+4+0+18 + 4 + 0 + 18+4+0+1,也就是我们熟悉的十进制中的131313。每个位置代表一个2的幂。这种位置结构是解锁计算能力的关键。

思考一下将一个十进制数乘以101010会发生什么。您只需将所有数字向左移动一位,并在末尾添加一个零。同样美妙的简洁性也适用于二进制。要将一个二进制数乘以222,您只需将其所有位向左移动一位。要乘以444(=22=2^2=22),您就移动两位。通常,乘以2k2^k2k等同于向左移位kkk个位置。除以2k2^k2k则是向右移位kkk个位置。

这不仅仅是一个巧妙的数学技巧,它是计算效率的基石。对于计算机而言,乘法和除法——这些对我们来说需要一系列步骤的运算——可以通过一次快如闪电的操作完成,其简单程度堪比移动电线。算术与移位比特这一物理行为之间的深刻联系是处理器设计的基石之一。对于负数,这个系统甚至更为优雅。一种特殊类型的右移,称为​​算术移位​​,会复制符号位以保持数字的符号,这在数学上执行的是​​向下取整除法​​——总是向负无穷方向取整。这确保了移位与乘除法之间的美妙对应关系对所有整数(无论正负)都成立。

驯服无限:处理有符号数和限制

与抽象的数学世界不同,物理世界存在限制。计算机没有无限数量的电线来表示一个数字。它使用固定大小的比特块工作,例如8位、32位或64位寄存器。这种有限性带来了一个挑战:我们如何表示负数,以及当我们的计算结果超出一个寄存器能容纳的最大值时会发生什么?

答案是一种非常巧妙的方案,称为​​补码​​。补码并非简单地用一位表示符号、其余位表示数值大小,而是将数字排列在一个圆环上。想象一个8位系统,它可以表示 28=2562^8 = 25628=256 个不同的值。我们让从000到127127127的数字表示它们自身。但是,当我们从127127127(二进制01111111)再加一时,我们得到的不是128128128。相反,模式10000000被定义为−128-128−128。从那里开始向上计数,10000001是−127-127−127,依此类推,直到11111111表示−1-1−1。

有限算术的这种循环特性最好通过一个类比来理解。想象一个使用8位补码数字来记录余额的财务账本,其中负值表示债务。你能拥有的最大贷方余额是127127127。如果你的余额是100100100,然后收到了两笔分别为505050和808080的交易,数学上的总和是230230230。但在我们的8位系统中,这个和会“环绕”这个圆。结果被解释为−26-26−26。这种现象,被称为​​溢出​​,不是数学上的一个错误;它是一个基本属性。计算机正在忠实地执行​​模2n2^n2n​​运算。这个原理解释了软件中无数的“小故障”,例如两个大的正数相加结果却是一个负数。这是将无限的数轴映射到一个有限的、循环的环上的直接后果。

为更宁静的世界编码:格雷码的天才之处

标准的二进制计数是合乎逻辑的,但它可能很“嘈杂”。当我们从3计数到4时,二进制表示从011跳变到100。在这一步中,所有三个比特同时翻转。在物理世界中,“同时”是一种错觉。电路中微小的延迟意味着这些比特可能在略微不同的时间改变。

现在,想象一下一个电路的两个部分运行在不同的、非同步的时钟上——这是复杂芯片中一个常见的情景,被称为异步接口。如果其中一部分试图在011到100的混乱转换期间精确地读取另一部分的计数器值,它可能会捕捉到一些已经翻转的比特和一些还没有翻转的比特。它可能会读到一个垃圾值,如110(6)或001(1),这是一个从未预期的值。这可能导致灾难性的系统故障。

为了解决这个问题,工程师们转向了一种不同的二进制表示法:​​格雷码​​。格雷码的决定性属性是任何两个连续的值仅在一个比特位上有所不同。例如,在一个3位格雷码中,从3计数到4可能是一个从010到110的转换。只有最高有效位发生了变化。现在,如果一个电路在此转换期间采样该值,最坏的情况是它读取到旧值(010)或新值(110)。两者都是有效状态。读取一个完全不相关的、虚假值的可能性被消除了。

从二进制到格雷码的转换本身就是一件美妙的事情,它依赖于​​异或(XOR)​​运算,这是一种基本的逻辑门。最高有效位保持不变(gn−1=bn−1g_{n-1} = b_{n-1}gn−1​=bn−1​),而每个其他的格雷码位gig_igi​仅仅是其对应的二进制位bib_ibi​与其左边的二进制位bi+1b_{i+1}bi+1​的异或结果(即,gi=bi+1⊕big_i = b_{i+1} \oplus b_igi​=bi+1​⊕bi​)。这个简单的规则允许直接高效的硬件实现。我们可以使用​​汉明距离​​来量化码字之间的这种“差异”,汉明距离就是两个二进制字符串不同位置的数量。格雷码的天才之处在于,任何两个相邻码字之间的汉明距离永远恰好为1。

使用“行话”:二进制世界的简写方式

虽然计算机能流利地使用二进制,但人类却不能。像11101.1011这样的一长串1和0写起来很麻烦,而且容易出错。为了弥合这一差距,我们使用对人类来说紧凑,但对机器来说转换回二进制又极其简单的数字系统。最常见的是​​八进制(基数为8)​​和​​十六进制(基数为16)​​。

这些基数如此方便的原因在于它们与基数2的关系:8=238 = 2^38=23 且 16=2416 = 2^416=24。这种数学上的亲缘关系意味着我们不需要做任何复杂的算术就能在它们之间进行转换。要将一个二进制数转换为八进制,你只需从小数点(基数点)开始,将比特分为三位一组。每组三位比特恰好对应一个八进制数字。例如,二进制011 101变成(35)8(35)_8(35)8​。对于十六进制,你则将比特分为四位一组。

这种简写在计算领域无处不在。一个数字信号处理器的指令手册可能会将一个操作码列为(53)_8,而不是更冗长的(101011)_2。这是相同的信息,只是以更人性化的方式呈现。然而,这种便利性可能会隐藏潜在的复杂性。同一个问题强调了一种场景,即硬件可能会以“反射”的顺序读取比特,这提醒我们,无论使用何种表示法,真正的物理现实是机器处理的二进制字符串。

表示法的陷阱:当二进制还不够时

二进制位置表示法的威力在于每个比特位都可以持有独立的信息。但这种独立性可能很脆弱。在计算机科学中最著名的证明之一——从顶点覆盖(VERTEX-COVER)问题到子集和(SUBSET-SUM)问题的归约中,这种脆弱性就凸显了出来。其目标是将一个关于图的问题转化为一个关于数字求和的问题。一个朴素的尝试可能是使用标准的基数2表示法,其中不同的比特位对应于原始问题中的不同约束。

想象一下尝试表示一个图问题,你需要构建一组大数。在一种提议的方案中,一个数字的第一部分代表一个顶点的选择,其他部分代表该顶点覆盖了哪些边。其思想是选择这些数字的一个子集,使其总和等于一个特定的目标值,从而解决该图问题。但在这里,一个致命的缺陷出现了:​​进位​​。如果你将两个数相加,而某个数字位置的和达到2或更大(在基数2中),就会产生一个进位,“溢出”到下一个位置。这个进位会破坏存储在相邻位置的信息,混淆逻辑,并允许出现“假阳性”解——即数字子集的总和达到了目标值,但并不对应于原始图问题的有效解。

解决方案是深刻的:问题不在于位置表示法的思想,而在于基数。通过使用更高的基数(如基数4),我们可以在数字位置之间创建“防火墙”。在基数4中,一个数字位可以容纳最大为3的值。如果我们设计的归约能确保任何数字位置的总和永远不会超过3,那么就永远不会产生进位。每个位置的信息都保持原始和隔离。这展示了一个优美而深刻的原则:数字系统的选择不仅仅是方便或效率的问题;它是一种构建信息结构的基本行为,错误的选择可能会破坏你试图实现的逻辑本身。

应用与跨学科联系

在我们之前的讨论中,我们探索了二进制系统的基本性质。我们看到它不仅仅是另一种计数方式,而是一种纯粹逻辑的语言,一个建立在最简单区别之上的系统:开或关,真或假,一或零。现在,让我们踏上一段旅程,看看这门简单的语言将我们带向何方。你可能会惊讶地发现,这些微不足道的比特是我们数字世界的建筑师,是复杂数据的沉默组织者,甚至是抵御物理世界混乱的守护者。我们将看到,二进制的原理并不仅限于数学教科书的页面;它们被编织在技术和科学的结构之中。

打包的艺术:用比特表示世界

从本质上讲,计算机的内存就像一个巨大的电灯开关面板。每个开关就是一个比特。因此,它最直接的应用就是表示一组“是/否”属性的集合。想一想计算机上一个文件的权限:用户可以读取它吗?可以写入吗?可以执行吗?对于文件所有者,我们有三个这样的问题;对于其所在组,有三个;对于其他所有人,也有三个。这就是九个独立的“是/否”问题。

我们可以使用一个单独的整数,而不是存储九个独立的布尔值。我们可以将这九个权限中的每一个分配给一个特定的比特位。如果第8位是“开”(1),用户可以读取;如果是“关”(0),则不能。如果第7位是“开”,他们可以写入,依此类推。因此,一个由其独特的九位模式表示的单一数字,可以同时容纳所有九个权限。例如,像“用户可以读写;组可以读和执行;其他人只能执行”这样的权限集,可以转换为二进制字符串110101001,它对应于整数425425425。这是一种极其紧凑和高效的状态管理方式,是各种操作系统和软件的基础技术。

这种“位打包”的思想可以扩展到远超简单标志位的范畴。想象你有一个大的数据网格,其中每个单元格不是真就是假——也许是机器人寻路算法的障碍物地图。我们可以将网格的每一行表示为单个整数,而不是存储一个庞大的布尔值数组。第一列的状态对应最低有效位,第二列对应下一个位,依此类推。一行64个单元格就变成一个单独的64位整数。这种通过将布尔状态映射到比特位来压缩数据的技术是高效数据表示的基石,使我们能够以惊人的速度存储和操作大量信息。

但是我们打包到这些比特中的不一定非得是简单的真或假。以魔方(Rubik's Cube)为例。它有八个角块,每个角块可以处于三种朝向状态之一(我们称之为0、1和2)。要存储所有八个角块的状态,你可能会认为需要八个数字。然而,魔方有一个美妙的机械秘密:这些朝向不是独立的。所有八个角块朝向的总和必须是三的倍数。这个物理约束意味着,如果你知道前七个角块的朝向,第八个角块的朝向就自动确定了!

这对于数据压缩来说是一份大礼。我们只需要存储七个独立的朝向。由于每个朝向需要表示三种状态之一,两个比特就足够了(21<3≤222^1 \lt 3 \le 2^221<3≤22)。因此,我们可以将七个角块的全部朝向状态打包到一个仅有 7×2=147 \times 2 = 147×2=14 比特的整数中。通过使用位运算将每个2位的值放入整数内各自的“字段”中,我们为魔方的状态创建了一个高度紧凑的表示。当我们需要知道完整状态时,我们解包存储的七个值,并使用模运算约束来计算第八个值。这是一个深刻的例子,说明了理解一个系统的物理原理如何让我们更优雅、更高效地表示其信息。

运动中的二进制:硬件、指令和算法

到目前为止,我们一直将整数视为静态的信息容器。但真正的魔力发生在我们操作它们的时候。计算机中的处理器以比特为单位进行思考,其核心指令——按位与(AND)、或(OR)、异或(XOR)和移位——都是为对这些位模式进行快如闪电的操作而设计的。

在硬件设计领域,我们经常需要控制电路的特定部分。处理器中的控制寄存器是一个整数,其单个比特或比特组充当不同硬件单元的开关。为了在不干扰其他部分的情况下只翻转正确的开关,我们使用“掩码”。掩码是另一个整数,经过精心构造,仅在我们想要更改的比特位置上为1。例如,在一个9位寄存器中,可能需要一个掩码来选择第3、4和5位。这个二进制掩码将是000111000。它经常被写成八进制的070并非巧合,因为每个八进制数字完美地映射到三位比特(8=238=2^38=23),这使其成为硬件工程师思考位域的便捷简写方式。

这种对位的直接操作是软件和硬件之间的桥梁。当程序员编写一行代码时,编译器会将其翻译成机器指令,而这些指令本身也只是二进制数。指令的一部分可能是一个“立即数”——一个直接嵌入指令位模式中的常数。当反汇编器向人类展示这条指令时,它可能会用八进制或十六进制来表示这个立即数。但现实中会出现一些实际问题。在许多指令集架构(ISA)中,为了节省空间,立即数可能会被硬件缩放。例如,一个用于寻址的立即数可能会被硬件自动左移两位,从而隐式地将其乘以四,因为内存地址通常按4字节边界对齐。这展示了二进制表示、物理硬件设计以及软件工具为理解这一切所使用的惯例之间的深刻相互作用。

直接用比特思考也为极其巧妙和高效的算法打开了大门,这些算法通常被称为“位操作技巧”。假设你需要找到一个数中最高有效'1'位的位置——这是数据结构和数值计算中的一个常见任务。你可以逐个比特地循环遍历,但这很慢。一种远为优雅的方法是向下传播最高有效位。通过一系列与该数右移2的递增次幂(1,2,4,8,…1, 2, 4, 8, \dots1,2,4,8,…)的结果进行或(OR)运算,最高的那个'1'位会向下“涂抹”,填满其下的所有比特位。对于像00101000这样的数,这个过程会将其转换为00111111。从这个结果掩码中,只需再用几个算术技巧就可以找到原始最高有效位的值和位置。这是一个在比特级别上进行并行计算的优美例子,用固定的几步就完成了朴素循环需要许多步才能完成的工作。

用格雷码驯服物理世界

标准二进制计数系统有一个奇特且有时危险的特性。当从3(011)计数到4(100)时,有三个比特同时改变。现在,想象一个机械传感器,比如一个测量轴角度的旋转编码器,它使用二进制码报告其位置。如果传感器正好悬停在3和4的边界上,它的探测器可能会在略微不同的时间读取到变化的比特。当比特转换时,它可能会瞬间报告一个完全错误的值,比如111(7)或000(0)。在高速系统中,这样的故障可能是灾难性的。

解决方案是一种名为格雷码的不同类型的二进制系统。其决定性特征是任何两个连续值仅在一个比特上不同。在格雷码系统中,从3到4的转换可能例如是从010到110。只有一个比特翻转。这个特性是救命稻草。从标准二进制到格雷码的转换非常优雅简单:对于每个比特,你将其与它左边的比特(下一个更高有效位)进行异或(XOR)运算。最高有效位保持不变。这可以在硬件中用一个简单的异或门级联来实现。

这个思想的力量在处理具有不同时间参考或“时钟域”的系统时最为明显。想象一下,芯片的一部分有一个快速运行的计数器生成时间戳,而芯片的另一部分较慢,需要读取这些时间戳。这是一个异步跨越。如果时间戳以标准二进制数发送,而读取器恰好在它变化时进行采样,由于多比特翻转问题,它可能会捕获一个无意义的值。

但如果时间戳首先被转换为格雷码,那么在任何给定时刻都只有一个比特在转换中。读取器可能会捕获到旧值或新值,但绝不会捕获到一个完全无效的“毛刺”值。这极大地控制了潜在的错误。当然,必须记住将捕获到的格雷码转换回二进制才能正确解释它。一个引人入胜的假设情景揭示了忘记这一步骤的危险:如果一个系统错误地将捕获的格雷码值直接解释为二进制数,由此产生的错误可能是巨大且系统性的,这恰恰是因为在两种系统中,相同数字的位模式是如此不同。格雷码并不能消除时序的不确定性,但它们驯服了它,确保其后果是有限且可预测的。

从管理文件权限到表示魔方的状态,从使用位掩码控制硬件到通过比特级并行执行算法,再到为物理系统创建抗错误编码,二进制数系统揭示了它的真正本质。它不仅仅是一种写数字的方式。它是一种用于编码逻辑、结构和状态的基本工具,是一种将抽象的软件世界与机器的物理现实统一起来的通用语言。