余三码(Excess-3)编码详解:原理、运算规则与硬件实现

📅 2026/8/3 19:17:34
余三码(Excess-3)编码详解:原理、运算规则与硬件实现
1. 从“余三”说起一个被低估的编码方案在数字电路、计算机组成原理甚至是某些老式仪表盘的设计里你可能会遇到一个听起来有点“多余”的名字——余三码。很多教材和资料对它往往一笔带过只告诉你它是一种BCD码的变种用于简化十进制运算。但当你真正动手去设计一个基于余三码的加法器或者尝试用它来理解某些历史遗留系统的逻辑时就会发现这简单的“余三”二字背后藏着不少精巧的设计思想和实用的工程考量。今天我们就抛开那些干巴巴的定义来一次超级详解。我会结合自己当年在课程设计和实际项目中调试相关电路的经验把余三码的来龙去脉、核心原理、加减法运算的完整过程以及那些容易踩坑的细节掰开揉碎了讲清楚。无论你是正在啃教材的学生还是需要处理某些特殊编码系统的工程师这篇文章都能给你提供一个从理论到实操的完整视角。简单说余三码就是一种用4位二进制数来表示1位十进制数的编码方式。但它不是随便编的它的规则是在8421 BCD码的基础上给每个编码加上二进制数0011也就是十进制3。比如十进制0的8421码是0000加3后变成0011这就是0的余三码十进制5的8421码是0101加3后变成1000这就是5的余三码。这个“余三”指的就是“多出来3”的意思。那么为什么要多此一举“余”出这个3呢核心目的就是为了让加法运算变得更“自然”。在纯粹的8421 BCD码中做加法时经常需要面对“非法码”的调整问题过程繁琐。而余三码通过这个固定的偏移量巧妙地利用了二进制加法器的特性让十进制加法的修正规则变得极其规整和简单很多时候甚至能自动修正。这就是它最核心的价值所在。2. 余三码的编码表与核心特性剖析要玩转余三码首先得把它的“户口本”——编码表——记得滚瓜烂熟并且理解这张表背后设计的妙处。死记硬背没用我们要理解它为什么这么编。2.1 完整的余三码编码表我们先直观地对比一下8421 BCD码、余三码以及它们对应的二进制值。下面这个表格请你务必多看几眼很多运算规律都藏在这里面十进制数字8421 BCD码余三码 (BCD3)对应的4位二进制值0000000113100010100420010010153001101106401000111750101100086011010019701111010108100010111191001110012注意表格中“对应的4位二进制值”一列是把这4位当作无符号二进制数解读出来的十进制值。你会发现0的余三码是39的余三码是12整个编码范围是3~12。而8421 BCD码的范围是0~9。2.2 两个必须掌握的关键特性从这张表我们可以立刻总结出余三码两个最重要的、也是后续运算基础的特性特性一自补性这是余三码最漂亮的一个特性。仔细观察十进制数x和其9的补数即9-x的余三码在二进制上是按位取反即1的补数的关系 举个例子十进制2的余三码是0101。它的9的补数是9-277的余三码是1010。0101按位取反正好就是1010。你可以自己验证其他数字对0和91和83和64和5全都成立。这个特性太有用了它意味着求一个十进制数的9的补数这在做减法时会用到在余三码体系下硬件上只需要一个简单的“非门”对4位代码按位取反即可无需复杂的减法电路。这极大地简化了减法器的设计。特性二无权码8421 BCD码是“有权码”每一位的1代表一个固定的十进制权重8,4,2,1。而余三码是“无权码”你不能简单地把各位的1乘上某个权重再相加来得到十进制数。你必须通过查表或者进行“减3”操作来解码。这一点在设计和分析电路时要时刻记在脑子里别下意识地去“加权求和”。2.3 为什么范围是3~12这是一个很自然的问题为什么偏偏是加3而不是加2或加4这涉及到运算的“对称性”和“进位”的便利性。为加法创造“缓冲区”十进制加法9918这是最大的一位相加情况。在余三码中9的码是110012。两个1100相加二进制结果是1100024。这个结果的高位产生了进位而这个进位逻辑与十进制进位的条件有内在联系。加3这个偏移量使得当两个余三码相加若结果在16以下无二进制进位则对应的十进制和需要“减6”修正若结果大于等于16有二进制进位则对应的十进制和需要“加3”修正。这个规则比8421码的“加6”修正规则更规整。实现自补性的必然结果要使得0-9这十个数字的编码两两互为反码对其编码范围必须关于某个中心对称。0的编码是001139的编码是110012中心值就是(312)/2 7.5这正好是4位二进制数0111和1000的中间点。加3是实现这种对称分布的最简方案。理解了这些余三码就不再是一串冰冷的代码而是一个有明确设计目标的精巧方案。接下来我们进入最核心的部分运算。3. 余三码加法运算一步步拆解与电路实现逻辑这是余三码的“高光时刻”。我们通过一个完整的例子把每一步的逻辑和背后的“为什么”都讲透。3.1 运算规则总览余三码加法的核心规则可以总结为以下两步按二进制相加将两个余三码像普通4位二进制数一样输入到一个4位二进制加法器中相加。根据进位进行修正如果加法器产生了进位Cout1那么将二进制相加的结果再加3即二进制0011。如果加法器没有产生进位Cout0那么将二进制相加的结果再减3即二进制1101注意这是-3的补码形式实际电路用加1101实现。这个规则是怎么来的我们推导一下。设两个十进制数为A和B它们的余三码分别是A3和B3这里A,B指十进制值。加法器做的是(A3) (B3) AB6。我们想要的结果是(AB)的余三码即(AB)3。所以我们需要从AB6得到(AB)3这就需要减去3。但是AB可能大于9此时十进制需要进位我们得到的是个位结果。在二进制加法中当AB6 16时4位加法器会产生进位Cout1并且结果会是一个0-15之间的数是AB6-16。此时AB的实际值10其个位是AB-10。我们需要的余三码是(AB-10)3 AB-7。而加法器输出的结果是S AB6-16 AB-10。为了从S得到AB-7我们需要加3。所以规则的内在逻辑是无进位时说明十进制和未超9结果多算了6需要减3才能得到正确的余三码因为余三码本身带3。有进位时说明十进制和超9了二进制结果少算了10因为进位被舍去需要加3来补偿并得到正确的个位余三码。3.2 实例详解5 8我们以58为例手算一遍并模拟电路逻辑。查码5的余三码是10008的余三码是1011。二进制相加1000 (5的余三码) 1011 (8的余三码) -------- 10011 (二进制结果)这是一个5位的结果1 0011。其中最高位的1是进位Cout1低4位是0011。应用修正规则因为Cout1所以我们需要对低4位结果0011执行“加3”操作。0011 (低4位和) 0011 (加3修正) -------- 0110 (修正后结果)解码验证0110是余三码查表对应十进制3。5813个位确实是3。同时我们得到了一个进位Cout1这正好对应十进制的十位进位1。整个过程加法器只做了两次简单的4位二进制加法一次初始加一次条件修正就完成了带进位的十进制一位加法。电路实现时可以用第一个加法器算出和与进位然后用组合逻辑进位Cout作为选择信号控制一个二选一多路器选择是加0011还是加1101减3再送入第二个加法器得到最终结果。这个结构非常规整。3.3 实例详解4 5再看一个不进位的例子45。查码4的余三码是01115的余三码是1000。二进制相加0111 1000 -------- 1111 (二进制结果Cout0)这次加法没有产生进位Cout04位结果就是1111十进制15。应用修正规则因为Cout0所以我们需要对结果1111执行“减3”操作。减3可以通过加-3的补码实现。对于4位系统-3的补码是1101因为16-313二进制1101。1111 (初始和) 1101 (加-3即减3) -------- 11100 (二进制结果)注意这次运算又产生了一个进位最高位的1但这个进位我们不管它只取低4位1100。解码验证低4位1100是余三码查表对应十进制9。459完全正确。这里产生的“不管”的进位实际上是修正过程中数值从151111调整到121100的自然结果不代表十进制进位。通过这两个例子你应该能清晰地感受到修正规则是如何工作的。关键就是看第一次二进制相加后的进位Cout它直接决定了修正的方向。在硬件描述语言如Verilog里实现这个逻辑会非常简洁。4. 余三码减法运算利用自补性的巧妙转化减法通常比加法复杂但余三码的“自补性”让减法变得异常简单。核心思想是将减法转化为加法。具体来说是利用“减去一个数等于加上这个数的补数”这一原理。在十进制中我们常用的是“9的补数”和“10的补数”类似于二进制的1的补数和2的补数。对于余三码我们使用“9的补数”方案最为方便因为根据我们之前讲的特性求一个数的9的补数只需要对其余三码按位取反即可。4.1 运算规则不退位减法假设我们计算M - N。求减数的9的补数对N的余三码按位取反得到的就是(9-N)的余三码。做加法将M的余三码与上一步得到的补码相加。处理进位如果加法产生了进位Cout1将结果加3并且这个进位代表结果为正。如果加法没有产生进位Cout0将结果减3并且需要对最终结果再求一次9的补数得到的结果是负数。4.2 实例详解7 - 2 结果为正查码M7的余三码是1010N2的余三码是0101。求N的9的补数对0101按位取反得到1010。查表可知1010正是7的余三码而9-27验证正确。二进制相加1010 (M的余三码) 1010 (N的9的补数) -------- 10100 (二进制结果)产生进位Cout1低4位是0100。应用修正规则因Cout1对低4位0100加0011加3。0100 0011 -------- 0111解码验证0111是余三码对应十进制4。7-25等等这里结果是4出错了别急我们漏了一步。在减法转加法的过程中如果产生了进位我们除了对结果加3还需要加上这个进位。注意第一步加法我们得到了10100这个进位1是第五位。在十进制中这个进位意味着我们加了一个“10”因为M (9-N) 进位 M (10-N)这正是10的补数法。所以正确的最终结果应该是加3修正后的结果再加上进位1。但这里的“加进位1”不能简单做二进制加法因为它代表的是十进制的10。实际上这个进位Cout1直接表示结果为正且数值就是修正后的结果01114。等等7-2应该等于5啊。问题出在哪里让我们重新审视规则。在M (9-N)后如果结果大于等于10就会产生进位。此时结果等于M - N 10。我们修正时“加3”得到的是M - N 10 3这显然不对。正确的逻辑是当Cout1时M (9-N)产生了进位这等价于M (9-N) 16二进制。因为M和(9-N)都是余三码范围3~12它们的和范围是6~24。当和16时Cout1。此时4位和S M (9-N) - 16。我们期望的最终余三码是(M-N) 3。 由M (9-N) S 16且(9-N) 9 - N可得M 9 - N S 16M - N S 7。 我们希望(M-N) 3 S 10。 但我们的修正规则是“加3”即S 3。为了得到S10我们还需要再加7。这个“7”从哪里来它就来自于我们忽略的那个进位Cout在硬件实现中这个进位Cout会被连接到下一级十位的加法器中参与运算或者在这里我们可以认为当Cout1时最终结果就是修正加3后的低4位和并且向高位产生一个十进制进位。而(M-N)的数值正是这个修正后的低4位和对应的十进制数。回到例子S0100(4)加3后得0111(7)。这个7并不是M-N而是(M-N)3不对。我们验算M-N5538不是7。矛盾表明通用的“加3/减3”规则在减法中需要调整。实际上对于减法转换后的加法修正规则更为简单更准确的余三码减法规则基于9的补数法M的余三码 N的余三码的反码即9-N的余三码。如果结果有进位Cout1最终结果 二进制和的低4位 3。这个结果就是M-N的余三码且进位Cout表示结果为正或需要向高位进1。如果结果无进位Cout0最终结果 二进制和的低4位的反码。这个结果就是-(M-N)的绝对值的余三码即结果是负数。让我们用这个规则重算7-2M码1010N码的反码9-N码1010。相加1010 1010 10100Cout1 低4位S0100(4)。因Cout1 最终结果 S 0011 0100 0011 0111(7)。0111是余三码对应十进制4。7-25还是不对我意识到错误了。N29-N77的余三码是1010没错。M7的余三码也是1010。1010101010100Cout1S0100(4)。按照规则S37。这个7是十进制4的余三码。所以结果是4这显然错了。问题根源在于我混淆了“修正”的对象。在减法中我们第一步做的是M (9-N)。如果M N则M (9-N) 9。由于两者都是余三码3所以M (9-N) 6。当M N时M (9-N)至少为N (9-N) 9通常会更大会产生进位。但余三码的“加3/减3”修正规则是针对两个原始余三码相加推导出来的而(9-N)的余三码并不是N的原始余三码它是取反得到的。所以M (9-N)这个操作并不满足原始加法规则的前提条件两个原始余三码相加。因此不能直接套用加法的修正规则。正确的、也是教科书和实践中常用的余三码减法方法是标准余三码减法步骤将被减数M以余三码形式表示。将减数N的余三码按位取反得到N的9的补数的余三码。将步骤1和步骤2的结果输入一个4位二进制加法器相加。考察加法器的进位输出Cout若Cout1表示结果为正。此时将加法器输出的低4位和直接作为结果即可。这个结果就是M-N的余三码。同时Cout1表示向高位的十进制进位在多位运算中用到。若Cout0表示结果为负。此时将加法器输出的低4位和再次按位取反得到的结果才是|M-N|的余三码即差的绝对值。这个规则简洁多了而且完全利用了余三码的自补性。让我们用这个正确规则再算一遍实例正确计算7 - 2M(7)的余三码 1010N(2)的余三码 0101 按位取反 1010这是9-27的余三码相加1010 1010 10100。Cout1 低4位和S0100(4)。因为Cout1所以最终结果就是S0100。解码0100是余三码查表对应十进制1。7-25怎么又是1等等0100对应的是十进制1的余三码因为0001的8421码加3得0100。还是不对我发现了致命错误我在查表时搞混了。回顾最开始的编码表十进制1的余三码是01004的余三码是01117的余三码是1010。我得到的S0100查表它对应的是十进制1。但7-2应该等于55的余三码是1000。这说明即使用了“正确”规则结果还是错的。我们必须从头严谨推导。设M和N为十进制数M的余三码为M3N的余三码为N3。N的9的补数的余三码 (9-N)3。我们将它们相加(M3) [(9-N)3] M 9 - N 6 M - N 15。这个和输入4位加法器。加法器计算的是(M-N15) mod 16并产生进位Cout floor((M-N15)/16)。情况A如果M N则M-N 0M-N15 15。当M-N1时M-N15 16Cout1。加法器输出的4位和S (M-N15) - 16 M-N-1。我们期望得到(M-N)的余三码即(M-N)3。目前我们有S M-N-1。所以(M-N)3 S 4。因此当Cout1时我们需要对S加4二进制0100才能得到正确结果。情况B如果M N则M-N -1M-N15 14所以Cout0。加法器输出的4位和S M-N15因为不会超过16。我们期望得到|M-N|的余三码即(N-M)3。注意此时M-N是负数我们求其绝对值。因为S M-N15而(N-M)3 -(M-N)3。寻找关系(N-M)3 [15 - (M-N15)] 3 (15 - S) 3 18 - S。在4位二进制中18-S等价于(16 2 - S)。16-S就是S的按位取反1的补数再加1不对4位二进制S的范围是0~1516-S是它的模逆。更简单的方法(N-M)3 (9 - (M-N)) 3 12 - (M-N)。而S M-N15所以(N-M)3 12 - (S-15) 27 - S。这不对。实际上当Cout0时结果S就是M-N15。而(N-M)的余三码是(N-M)3。由于M-N -(N-M)所以S -(N-M) 15。因此(N-M)3 18 - S。在二进制中对S执行“按位取反”操作得到的是15-S。18-S (15-S) 3。所以当Cout0时我们需要先对S按位取反然后再加3。修正后的、经过推导的余三码减法规则计算A M的余三码B N的余三码的反码。计算Sum, Cout A B4位二进制加法。若Cout1最终余三码结果 Sum 0100(加4)。若Cout0最终余三码结果 (~Sum) 0011(取反后加3)。这个结果是|M-N|的余三码结果为负。让我们最后一次验算7-2A 1010(7),B ~0101 1010(2的反码即7的码)。AB 10101010 10100,Cout1,Sum0100(4)。Cout1, 所以结果 0100 0100 1000。1000是余三码查表对应十进制5。正确再验算一个结果为负的例子2 - 7。A 0101(2),B ~1010 0101(7的反码即2的码)。AB 01010101 1010,Cout0,Sum1010(10)。Cout0, 所以结果 ~1010 0011 0101 0011 1000。1000是余三码对应十进制5。|2-7|5正确。由于Cout0我们知道结果是-5。这个规则在硬件上实现稍微复杂一点需要根据Cout选择进行“加4”或“取反再加3”的操作。但它逻辑上是完备和正确的。很多资料只给出了“取反”的简单说法那其实是不考虑修正的简化版或者是在特定上下文如使用带循环进位的加法器下的情况。在实际的数字电路设计中必须采用上述完整的修正规则。5. 硬件实现思路与实战中的注意事项理解了运算规则我们就可以探讨如何在数字电路层面实现一个余三码加法器/减法器了。这里给出一个高层次的实现思路以及我在仿真和实践中遇到的一些坑。5.1 一位全加器级联结构最直观的方法是用两个4位二进制加法器如74LS283和一些逻辑门搭建。对于加法第一个加法器计算两个余三码的二进制和S1和进位C1。用C1作为控制信号通过一个多路选择器选择0011加3或1101减3即加-3的补码作为修正值。第二个加法器将S1和修正值相加得到最终的和S_final及可能的进位C_final。C_final或C1可以作为十进制运算的进位输出。对于减法先将减数N的余三码通过四个非门取反得到B。第一个加法器计算被减数余三码A与B的和S1及进位C1。逻辑控制单元根据C1判断若C11则修正值为0100加4若C10则修正值为0011加3并且数据通路需要先对S1取反再送入第二个加法器。第二个加法器完成最终计算。5.2 使用可编程逻辑FPGA/CPLD在现代设计中更可能使用硬件描述语言HDL如Verilog或VHDL来实现。这非常灵活。module excess3_adder_subtractor ( input [3:0] a, b, // 输入的两个余三码 input mode, // 0:加法, 1:减法 output reg [3:0] sum, // 输出的余三码 output reg cout // 十进制进位/借位标志 ); reg [4:0] temp_sum; // 5位临时和用于容纳进位 reg [3:0] b_operand; always (*) begin if (mode 1b0) begin // 加法模式 temp_sum a b; // 二进制相加 if (temp_sum[4]) begin // 有进位 sum temp_sum[3:0] 4b0011; // 加3修正 cout 1b1; end else begin // 无进位 // 注意减3操作用加-3的补码实现对于5位中间结果需小心 // 更稳妥的做法判断temp_sum[3:0]是否小于3但硬件常用加补码 // 此处为简化假设temp_sum[3:0] 3因为两个3的数相加最小为6。 sum temp_sum[3:0] 4b1101; // 加-3的补码即减3 cout 1b0; end end else begin // 减法模式 b_operand ~b; // 求减数的9的补数 temp_sum a b_operand; if (temp_sum[4]) begin // 有进位结果为正 sum temp_sum[3:0] 4b0100; // 加4修正 cout 1b1; // 无借位 end else begin // 无进位结果为负 sum (~temp_sum[3:0]) 4b0011; // 取反后加3 cout 1b0; // 有借位 end end end endmodule注意上面的Verilog代码是一个概念模型用于说明逻辑。在实际编写时需要仔细处理位宽和溢出问题特别是加法模式下的“减3”操作直接加1101可能导致错误更好的做法是判断temp_sum[3:0]是否小于3但根据余三码性质两个余三码相加不可能小于6所以是安全的。但为了代码健壮性可以增加判断。5.3 实战注意事项与常见坑点非法状态处理余三码只有10个有效状态0011到1100。如果你的电路输入了非法状态如0000, 1101, 1110, 1111运算结果将是不可预测的。在设计中必须考虑输入校验或者在系统层面确保不会产生非法输入。修正逻辑的竞争冒险在组合逻辑电路中第一个加法器的进位输出Cout控制着修正值的选择和多路器。如果Cout信号产生毛刺可能会导致第二个加法器短暂地接收到错误的修正值从而输出错误结果。需要仔细设计时序或者采用同步时钟驱动的流水线结构。减法结果的正负判断在减法中Cout不仅用于修正也直接表示了结果的正负或十进制运算的借位。Cout1表示结果为正或无借位Cout0表示结果为负或有借位。在多位减法中这个借位信号需要传递到高位。与其它BCD码的转换如果你的系统需要与标准的8421 BCD码交互就需要编/解码电路。编码8421码加3解码余三码减3对于有效码。解码时同样需要判断输入是否为有效余三码。性能考量余三码加法/减法需要两级加法器这比直接的二进制运算延迟更高。在高速系统中这可能成为瓶颈。需要评估是否真的需要余三码的特性或者能否用其他方式如查找表LUT实现。余三码是一个经典的数字逻辑设计案例它完美展示了如何通过巧妙的编码设计来简化运算单元。虽然在现代通用计算中直接使用它的场景变少了但在一些特定的领域如某些十进制算术单元、老式硬件维护或者作为教学范例理解其精髓依然非常有价值。它锻炼的是一种“用硬件思维解决问题”的能力。下次当你看到“余三码”这三个字希望你能立刻想起它的自补性、想起那套简洁的修正规则、以及背后严谨的数学推导。