二进制编码十进制(BCD)是一种编码十进制数字的方式,其中每个数字由其自己的二进制序列表示。与纯二进制表示不同,BCD 保留了精确的十进制值——这使其在精度比存储效率更重要的金融系统、支付终端和 HSM 通信中至关重要。
什么是 BCD?
在标准二进制中,数字 255 存储为 11111111(一个字节)。在 BCD 中,每个十进制数字单独编码:
十进制: 255
二进制: 11111111 (一个字节,值 255)
BCD: 0000 0010 0101 0101 (两个字节,每个半字节 = 一个数字)
BCD 比纯二进制占用更多空间,但它有一个关键优势:精确的十进制表示,没有舍入误差。这就是 BCD 在金融和支付系统中占主导地位的原因。
压缩 BCD vs 非压缩 BCD
BCD 有两种主要格式:
压缩 BCD(Packed BCD)
每个字节存储两个十进制数字——一个在高半字节(4 位),一个在低半字节:
数字: 92
压缩 BCD: 1001 0010 (0x92)
数字: 1234
压缩 BCD: 0001 0010 0011 0100 (0x1234)
优势:
- 更紧凑(每字节 2 个数字)
- 支付和金融系统中的标准
- 用于 ISO 8583 字段、卡磁道数据、PIN 块
非压缩 BCD(Unpacked BCD)
每个字节在低半字节存储一个十进制数字,高半字节通常设为 0(有时为 0xF):
数字: 92
非压缩 BCD: 0x09 0x02 (两个字节)
数字: 1234
非压缩 BCD: 0x01 0x02 0x03 0x04 (四个字节)
优势:
- 处理更简单(每字节一个数字)
- 用于某些遗留系统和特定协议
- 常见于基于 EBCDIC 的系统
BCD 编码工作原理
十进制转换为压缩 BCD
对于每对十进制数字,将第一个数字放在高半字节,第二个放在低半字节:
十进制: 78
数字 7 → 0111 (高半字节)
数字 8 → 1000 (低半字节)
压缩 BCD: 01111000 = 0x78
十进制转换为非压缩 BCD
每个数字占一个字节:
十进制: 78
数字 7 → 00000111 = 0x07
数字 8 → 00001000 = 0x08
非压缩 BCD: 0x07 0x08
处理奇数长度数字
对于奇数位数的压缩 BCD,前导半字节填充 0:
十进制: 123
压缩 BCD: 0001 0010 0011 → 0x0123 (前导零半字节)
BCD vs 十六进制 vs 二进制
理解差异至关重要:
| 表示方式 | 数字 1234 | 使用字节数 | 保留十进制 |
|---|---|---|---|
| 二进制 | 0x04D2 | 2 | 否(十六进制是 04D2) |
| 压缩 BCD | 0x1234 | 2 | 是(看起来像 1234) |
| 非压缩 BCD | 0x01 0x02 0x03 0x04 | 4 | 是 |
| ASCII | 0x31 0x32 0x33 0x34 | 4 | 是 |
关键区别:
- 二进制:最紧凑,但十进制值不直接映射到十六进制表示
- 压缩 BCD:紧凑的十进制表示,每字节 = 2 个十进制数字
- 非压缩 BCD:每字节一个数字,更易处理
- ASCII:人类可读,但占用更多空间
BCD 在金融系统中的应用
ISO 8583 支付消息
ISO 8583 广泛使用 BCD:
- 字段 2(PAN):压缩 BCD,左对齐,奇数长度用 F 填充
- 字段 7(传输日期/时间):压缩 BCD,MMDDhhmmss
- 字段 11(系统跟踪审计号):压缩 BCD,6 位数字
- 字段 12/13(本地交易时间/日期):压缩 BCD
PAN 字段示例:
PAN: 4111111111111111 (16 位数字)
BCD: 0x41 0x11 0x11 0x11 0x11 0x11 0x11 0x11
卡磁道数据
磁条磁道数据使用 BCD:
- 磁道 1:字母数字(非 BCD)
- 磁道 2:仅数字,BCD 编码
- 磁道 3:仅数字,BCD 编码
PIN 块
PIN 块(ISO 9564)对 PIN 数字使用 BCD:
PIN: 1234
BCD 格式: 0x04 0x12 0x3F 0xFF 0xFF 0xFF 0xFF 0xFF
↑ ↑
长度 PIN 数字用 F 填充
EMV 芯片数据
EMV(芯片卡)数据编码对数字字段使用 BCD:
- 金额:6 字节压缩 BCD
- 日期:3 字节压缩 BCD(YYMMDD)
- 时间:3 字节压缩 BCD(HHmmss)
BCD 运算
BCD 算术与二进制算术不同。当加 BCD 数字时:
0x09 (9)
+ 0x01 (1)
= 0x0A (无效 BCD!)
结果 0x0A 不是有效的 BCD。需要加 6 来修正:
0x0A
+ 0x06
= 0x10 (BCD 中的 10 = 进位 + 0)
这就是为什么 BCD 算术比二进制算术慢但保留十进制精度的原因。
有符号 BCD
BCD 可以使用符号-数值或十进制补码表示负数:
符号-数值
最后一个半字节表示符号:
0xC= 正0xD= 负
+1234: 0x1234C
-1234: 0x1234D
十进制补码
类似于二进制中的二进制补码,但用于十进制。在支付系统中不太常见。
HSM 命令中的 BCD
HSM(硬件安全模块)经常使用 BCD:
- PIN 块格式(ISO 9564)
- DUKPT 中的密钥序列号(KSN)
- 交易数据中的金额字段
- 日期/时间字段
向 HSM 发送命令时,必须将数字字段编码为 BCD,而不是 ASCII 或二进制。
常见 BCD 陷阱
无效 BCD 值
每个半字节必须是 0-9。值 0xA-0xF 在标准 BCD 中无效:
有效 BCD: 0x12 0x34 0x56 (123456)
无效 BCD: 0x12 0x3A 0x56 (A 不是十进制数字)
字节顺序混淆
BCD 没有固有的字节顺序问题(数字序列始终是大端序),但包含消息可能对多字节字段使用不同的字节顺序。
填充歧义
不同系统使用不同的填充:
- F 填充:
0x12 0x3F(123 用 F 填充) - 零填充:
0x12 0x30(123 用零填充) - 空格填充:
0x12 0x32 0x00(123 用空值填充)
始终检查您的协议规范。
在线工具
使用 BCD 转换器:
- 在十进制和压缩/非压缩 BCD 之间转换
- 使用不同填充选项编码和解码 BCD 值
- 验证 BCD 数据并识别无效半字节值
- 理解支付系统如何编码数字字段
所有处理都在浏览器中进行——您的数据永远不会离开您的设备。