简述: 二进制编码十进制(BCD)用一个 4 位半字节存储每个十进制数字 0–9,编码 1010–1111 不使用。当未经校正的二进制和大于 9 或产生进位输出时,BCD 一位加法器执行+6 校正。BCD 能精确保存已编码的十进制数字,但有限精度的除法与定标仍需要明确的舍入规则。
二进制与十进制位值的质因数不同,因此许多有限十进制小数——包括 0.1——都不存在有限的二进制展开。二进制编码十进制直接存储十进制数字,因而可避免初始的二进制小数表示误差;但当运算结果所需位数超出格式容量时,可靠的应用仍须明确定标、精度和舍入规则。
什么是二进制编码十进制?
BCD 使用各自独立的四位二进制码表示十进制数字 0 到 9。四位可编码 16 种模式,但 BCD 只使用前十种:
| 十进制 | BCD 半字节 |
|---|---|
| 0 | 0000 |
| 1 | 0001 |
| 2 | 0010 |
| 3 | 0011 |
| 4 | 0100 |
| 5 | 0101 |
| 6 | 0110 |
| 7 | 0111 |
| 8 | 1000 |
| 9 | 1001 |
剩下的六种模式 1010 到 1111——在纯二进制中即数值 10 到 15——在 BCD 中是无效的。产生其中一种模式的电路,要么发生了数字溢出,要么接收到格式错误的输入;BCD 加法器必须检测并校正这种情况。
多位十进制数由多个半字节串联编码。例如,数字 327 表示为:
与之相比,纯二进制表示 使用 9 位而非 12 位。BCD 用额外位数换取以数字为中心的表示方式,并简化某些十进制接口。
压缩 BCD 与非压缩 BCD
有两种常见的存储布局。
非压缩 BCD每字节存储一个十进制数字。低半字节保存数字;高半字节为零,或携带类似 ASCII 的标记(早期 IBM 和 x86 BCD 指令中为 0x30,因而可直接生成可打印的 ASCII 数字)。这种格式仅利用一半存储位,但输出为字符十分直接。
压缩 BCD每字节存储两个十进制数字,每个半字节各存一个。数字 327 的压缩 BCD 需要两个字节:0x03 0x27 。压缩 BCD 的密度是前者两倍,也是硬件 BCD 加法器和 x86 DAA/DAS 指令所使用的格式。
| 格式 | 数字 327 | 字节数 | 密度 |
|---|---|---|---|
| 非压缩 | 0x03 0x02 0x07 | 3 | 1 个数字/字节 |
| 压缩 | 0x03 0x27 | 2 | 2 个数字/字节 |
| 纯二进制(16 位) | 0x01 0x47 | 2 | 完整范围 |
对于可装入 16 位的数值(最大 65,535),纯二进制与压缩 BCD 的密度相同,却能编码 65,536 个不同数值,而不是 10,000 个。只有在必须执行精确匹配十进制的算术时,成本才会再次显现。
BCD 为什么仍然存在?
若二进制更密集,为何还要使用 BCD?有三个原因使 BCD 得以延续。
精确存储十进制数字。 十进制的 0.1 没有终止的二进制表示。当十进制小数点位置是格式的一部分时,BCD 可以精确存储 0.1 。只有当数学结果适合所选数字位数和小数位时,算术才是精确的;除法及其他非终止结果仍需要舍入。IEEE 754 浮点数介绍了这种权衡的二进制一面。
显示转换简单。 数字时钟或计算器可通过小型查找译码器,将每个 BCD 半字节转换为七段显示器的驱动信号,无需执行除以 10、循环减法或软件格式转换。后文介绍的 BCD 转七段译码器正是这一用途。
十进制业务规则。 金融和计量应用通常规定十进制小数位和舍入方式。压缩十进制、COBOL COMP-3 、十进制浮点数和缩放二进制整数都是可行的表示法。当数字级存储或接口很重要时,BCD 很有用;法规并不普遍强制使用 BCD。
BCD 数字相加:+6 校正
BCD 加法使用普通的四位二进制加法器——可参阅四位行波进位加法器文章复习——然后接一个十进制校正阶段,用于修正大于 9 的结果。
规则很简单。将两个 BCD 数字当作四位二进制数相加,然后:
- 若二进制和小于或等于 9,并且没有进位输出,则 BCD 结果保持该二进制和不变。
- 若二进制和大于 9,或四位加法器产生了进位输出,则加 6(
0110)进行校正,并向下一数字传递进位。
为何是 6?因为 BCD 跳过了六种无效模式 1010–1111。落在该范围内或超出该范围的任何二进制和,在 BCD 编号中都恰好超前了六个位置;加 6 会使它绕回,并产生十进制算术所期望的进位。
例题:7 + 5
对操作数编码并按四位二进制相加:
结果 1100 在二进制中是 12,属于无效 BCD 模式之一。应用 +6 校正:
校正后的结果为 0001 0010,即 12 的 BCD 表示——十位上的进位和个位上的 2。这正是十进制算术应产生的结果。
例题:8 + 9
四位加法器产生 0001 并带有进位输出。即使 0001 看似有效的 BCD 数字,进位输出仍会触发 +6 校正:
最终结果为 0001 0111,即 17 的 BCD 表示。若不校正,串接原始进位与低半字节将得到 0001 0001,BCD 译码器会将其读为 11,而不是 17。
“需要校正”的通用检测条件为:
其中 是四位二进制和。两个 AND 项合起来覆盖六种无效模式 1010–1111,而 捕捉和已绕过 15 的情况。
硬件:单数字 BCD 加法器
单数字 BCD 加法器由两个四位二进制加法器和一小段校正检测电路构成:
A[3:0] B[3:0]
| |
v v
+-----------------+
| 4-bit adder #1 | <-- ordinary binary add (see /docs/adder)
+-----------------+
S[3:0] C_out
| |
+--+--+--+
| |
v v
+----------+
| detect | --> adjust = C_out OR (S3·S2) OR (S3·S1)
+----------+
|
v 0110 if adjust, else 0000
+-----------------+
| 4-bit adder #2 | <-- adds the correction
+-----------------+
|
v
BCD result, BCD carry-out
第一个加法器执行原始二进制加法。检测模块计算 adjust 信号。第二个加法器无条件地向中间和加上 0 或 6。BCD 进位输出就是 adjust 信号本身:每当需要加 6 时,也会向下一个十进制数字传递一个 1。将 个这样的模块菊花链连接,就得到一个 位 BCD 行波加法器。该结构类似二进制全加器与半加器链,只是数字更宽,且多了校正阶段。
此类电路可由两个加法器模块和校正检测逻辑构成,再由数字显示器显示结果。输入应限制为有效的 BCD 数字 0–9;该校正表达式不能作为无效输入半字节的通用纠错逻辑。
BCD 减法
减法遵循相同模式,但以9 的补码或10 的补码代替二进制的 2 的补码。计算 时:
- 将每个数字 替换为 ,构造 的 9 的补码。
- 将结果作为 BCD 加法加到 。
- 若出现最终进位输出,向结果加 1(10 的补码校正)。该进位表示结果为正。
- 若没有进位输出,结果为负;重新取补码以恢复其绝对值。
9 的补码之于十进制,如同 1 的补码之于二进制;10 的补码则是 2 的补码的十进制对应物。BCD ALU 只需在减法的另一侧使用相同的校正阶段。
BCD 的缺点
其优点伴随着实际成本。
存储开销。 每个十进制数字需要 4 位,因此 4 个字节最多编码 99,999,999。相同的 32 位若采用纯二进制,可表示最高约 42.9 亿的数值,其数值范围约为前者的 43 倍。对于科学计算或图形处理,BCD 的存储效率通常较低。
更多算术工作。 每次数字相加都需要二进制加法、校正检测和校正。若干旧版 x86 十进制校正指令在 64 位模式中不可用。压缩十进制、十进制浮点数还是缩放整数表现最佳,取决于 ISA、工作负载、库和所需语义。
没有原生小数支持。 这里描述的 BCD 编码整数。小数 BCD 需要按约定固定十进制点位置,或改用密集压缩十进制或 IEEE 754-2008 十进制浮点数;后两者以更紧凑的代码将数字每三位一组编码。这些编码的历史可追溯到最早的二进制数制,以及关于以十还是以二计数的长期争论。
BCD 仍存在哪些地方
只要输入与输出是十进制,BCD 就依然存在,即使计算本身未必如此。
- 数字时钟和手表。 一只 24 小时时钟将时、分、秒存储为六个 BCD 数字。计数器芯片递增秒数;小型比较器检测到 60 时滚入分钟;同样模式处理 60 分钟和 24 小时。每个数字都经由 BCD 至七段译码器驱动七段 LED。
- 计算器。 以数字为中心的算术可简化十进制输入与显示,尽管计算器实现也会使用二进制和其他内部表示。
- 工业控制面板。 拇指轮开关直接输出 BCD,PLC 输入无需转换即可读取 BCD。
- 企业系统。 支持压缩十进制的体系结构和语言,对于固定小数位业务数据和 COBOL 工作负载仍有实用价值。
- 仪表与销售点设备。 十进制显示和规定的舍入规则可使数字导向格式更方便,尽管具体实现选择有所不同。
这一教训可以推广。当问题领域天然是十进制时,显式编码数字往往优于在每次运算中进行进制转换。这一逻辑也支持今天在货币处理软件中使用定点整数算术,即使底层 CPU 完全按二进制工作。
在仿真器中验证
一项清晰的练习是:构建单数字 BCD 加法器,并观察校正如何发生。
- 将两个四位输入寄存器 与 接入一个加法器模块,显示原始五位和。
- 构建检测逻辑:对 、 和 进行 OR。
- 使用检测信号将常数 0110 门控到第二个加法器,并与第一个和相加。
- 将校正结果的低半字节接到数字显示器,再将 BCD 进位输出接到单独的一位显示器。
- 扫描 与 。确认两位显示器显示 0–18,且永远不会出现 A–F 符号。
对于更宽的数值,串接第二个 BCD 加法器模块。第一个模块的进位输出成为第二个模块的进位输入,就像二进制行波链一样。
后续阅读
单数字 BCD 加法器需要配合可读的显示结果。BCD 转七段译码器:从数字到显示从 BCD 真值表推导各段驱动信号的布尔表达式,并在此前介绍的译码器基础上说明如何连接实际显示器。IEEE 754 浮点数则从二进制表示角度说明二进制小数的设计依据,以及金融软件持续采用十进制表示的原因。
打开四位二进制计数器模板,为 BCD 加法器提供递增输入,并观察每逢第十个时钟节拍触发校正——这是观察+6 效果最直接的方式。



