深入理解计算机系统:CPU 里面根本没有减法器?揭秘计算机的 0 和 1 是如何计算的

发布时间:2026/7/27 20:15:00

深入理解计算机系统:CPU 里面根本没有减法器?揭秘计算机的 0 和 1 是如何计算的 第一部分计算机是如何“认识”和“存储”整数的计算机的底层世界里只有0和1所有的数学运算最终都要转换成对二进制位的操作。理解计算机的整数运算是深入学习编程比如理解为什么数字会溢出、位运算怎么用的必经之路。假设我们的计算机非常简陋容量只有 4 个比特bit。4 个比特意味着只能组合出 16 种状态从0000到1111。所以这个表盘上只有 16 个刻度。加法是什么就是在表盘上顺时针往前拨动指针。截断溢出是什么就是指针转了一圈越界了又绕回了起点。如果你把这个表盘当作“无符号数”来读它的刻度就是正常的0 到 15。假设指针现在指着 151111你要计算15 1。指针顺时针走 1 格啪越过了最高点掉回了底部的00000。结果就是 15 1 0。这就叫溢出加出来的结果反而变小了。为了表示负数我们把表盘一分为二右半边0 到 7标签保持不变。左半边本来是 8 到 15 的位置我们给它们撕掉原来的标签换上-8 到 -1的标签。现在会发生什么奇妙的事情假设指针指着 70111你计算7 1。指针顺时针走 1 格来到了1000这个位置。在有符号数的标签体系里1000对应的标签是-8结果就是 7 1 -8。这就是正溢出一直加正数走着走着突然掉进了负数的领地。为了后续讲解方便说明我们统一假设计算机使用 8位8-bit 来存储一个整数即一个字节的长度。1. 最简单的形态无符号整数 (Unsigned Integer)无符号整数是指不包含负数的整数只有 0 和正数。在这种表示方法下8个位置的每一个0或1都用来表示数值的大小。实例十进制的5。计算方法5 4 1在二进制中即为00000101。范围8位全为000000000(十进制的 0)8位全为111111111(十进制的 255)所以8位无符号整数的范围是 0 到 255。2. 带符号整数的痛点原码 (Sign-Magnitude)现实世界中不仅有正数还有负数。计算机科学家最初的思路很直观拿出最左边的一位最高位作为符号位。0代表正数 ()1代表负数 (-) 剩下的 7 位用来表示数字的绝对值大小。这种表示法叫做原码。实例正数5符号位是 0数值是 5。原码为00000101。 负数-5符号位是 1数值是 5。原码为10000101。原码的致命缺陷有两个零0的原码是00000000-0的原码是10000000。在数学上 0 是唯一的计算机存两个 0 会导致判断相等时非常麻烦。运算复杂如果直接把5和-5的原码加起来000001011000010110001010(对应十进制的 -10)但这显然不对结果应该是 0。这意味着计算机在做加减法时必须先判断符号再决定是做加法还是减法硬件设计会变得非常复杂。3. 过渡方案反码 (Ones Complement)为了解决原码在相加时出错的问题科学家发明了反码。正数反码和原码一模一样。负数符号位保持为1其余的 7 位全部取反即0变11变0。实例-5的原码10000101-5的反码11111010(最左边的1不变后面的 0000101 变成了 1111010)反码解决了一部分运算问题但依然存在两个零的缺陷0的反码是00000000-0的反码是11111111。4. 最终的王者补码 (Twos Complement)补码是目前所有现代计算机都在使用的整数存储方式。它完美解决了原码和反码的缺陷正数补码和原码、反码一模一样。负数在反码的基础上加 1。实例推导如何求-5的补码先写出绝对值 5 的原码00000101符号位改为1变成 -5 的原码10000101负数取反码符号位不变其余取反11111010负数取补码反码加111111011所以-5在计算机内存中的真实样子是11111011。补码为什么伟大唯一的零0补码是00000000。-0从反码11111111加 1 后发生进位最高位溢出丢弃结果依然是00000000。零终于唯一了加减法统一计算机可以用同一套加法电路来处理正数、负数的加法和减法极大地简化了 CPU 的设计我们将在第二部分详细演示。第二部分计算机是如何计算的在上一部分我们知道了计算机最终选择了补码来存储整数。你可能会问为什么要费这么大劲绕过原码和反码非要用补码呢1. 补码的加法连带符号一起加在日常数学中正数加负数我们需要先判断谁的绝对值大然后再做减法。但在补码的世界里符号位也是数字的一部分直接按位对齐相加即可。规则将两个数的 8 位补码按位对齐相加逢二进一。如果最高位第 8 位相加后产生了进位这个多出来的第 9 位会直接被丢弃因为我们的盒子只有 8 位。实例计算5 (-3)5的补码是00000101-3的补码是11111101推导过程3的原码00000011- 反码11111100- 补码加111111101我们把它们像小学加法一样列竖式相加00000101 (5) 11111101 (-3) ----------------- 1 00000010最高位的1溢出了 8 位的边界直接丢弃。剩下的结果是00000010转换为十进制正好是2 完全不需要单独处理负号加法电路自己就得出了正确答案。2. 减法就是加法计算机没有减法器因为补码的加法太好用了现代计算机的 CPU 内部根本没有专门的“减法电路”。 当计算机看到减法指令A - B时它会直接将其转化为加法A (-B)。实例计算7 - 2计算机不会直接做减法而是把它变成7 (-2)。 它会先求出 2 的补码再对其求负即取反加1得到 -2 的补码最后用加法器把 7 的补码和 -2 的补码加在一起。3. 溢出 (Overflow)当盒子装不下时8 位带符号整数的表示范围是-128到127。 你有没有想过如果我在编程时让127 1计算机会输出什么会是 128 吗实例计算127 1127的补码是01111111最大的正数1的补码是00000001按位相加01111111 (127) 00000001 (1) ----------------- 10000000结果变成了10000000。 还记得第一部分吗最高位是 1 代表负数。在 8 位补码中10000000规定的十进制值是-128 所以在 8 位计算机的眼中127 1 -128。这就是溢出。当计算结果超出了数据类型能容纳的最大范围时数字会“绕圈子”正数加正数可能变成负数负数加负数也可能变成正数。这在实际编程中是一个极其危险的错误被称为 Integer Overflow可能导致程序崩溃或安全漏洞。第三部分计算机的位运算与移位在前两部分我们把 8 个二进制位bit看作一个整体来表示数字。但计算机最基础的硬件是逻辑门电路它真正的强项是对每一个单独的0和1进行独立操作。这就是所谓的“位运算”Bitwise Operations。掌握位运算不仅能写出运行极快的代码还能帮你理解图像处理、密码学和网络协议的底层原理。1. 基础位运算与、或、非、异或位运算直接作用于二进制位就像对每一位做选择题。我们先看四个最基础的操作。① 按位非 (NOT /~)黑白颠倒这是最简单的单目运算只对一个数操作。规则很简单0变11变0。实例对00001111取非。~ 00001111的结果是11110000。② 按位与 (AND /)严苛的门卫把两个数的二进制位上下对齐只有当两个位都是1时结果才是1只要有一个0结果就是0。实际用途通常用来充当“掩码”Mask提取或保留特定的位把其他位清零。实例提取10110010的后 4 位。 我们构造一个掩码00001111前4位0后4位1然后做 AND 运算10110010 00001111 ----------------- 00000010 (前4位被无情清零后4位完美保留)③ 按位或 (OR /|)宽容的合并上下对齐只要两个位中有一个是1结果就是1只有全是0才是0。实际用途用来将某些特定的位强制设置为1。实例强制把01000001的最高位设为1。 构造一个数10000000做 OR 运算01000001 | 10000000 ----------------- 11000001④ 按位异或 (XOR /^)找不同专家上下对齐相同为0不同为1。实际用途异或有一个神奇的特性任何数和自己异或都是 0任何数和 0 异或都是自己。这常用于简单的加密和翻转特定位。实例翻转11110000的前 4 位。 构造掩码11110000做 XOR 运算11110000 ^ 11110000 ----------------- 00000000 (相同的位变成了0)2. 移位操作快速乘除法移位操作Shift是指把二进制数字整体向左或向右移动。在底层这比使用乘法器或除法器电路要快得多左移 ()乘以 2 的 N 次方将所有位向左移动指定的位数。左边溢出的位丢弃右边空出来的位置永远补0。实例00000101(十进制的 5) 左移 1 位写为5 1。整体左移右侧补0变成00001010(十进制的 10)。相当于。右移 ()除以 2 的 N 次方将所有位向右移动。右边溢出的位直接丢弃。但左边空出来的位置补什么呢这里分为两种情况逻辑右移无符号数左边一律补0。算术右移带符号数为了保持正负号不变左边补符号位的值。如果原来是正数最高位0就补0如果原来是负数最高位1就补1。实例带符号负数11111000(-8) 算术右移 1 位。整体右移左侧空位补符号位1变成11111100(-4)。相当于。

相关新闻