# BCD（二进制编码十进制）：压缩、非压缩与金融消息

> BCD 编码实用指南——压缩与非压缩格式、ISO 8583 金融消息中的应用，以及 BCD 与十六进制之间的转换。

Canonical: https://hsmkit.com/zh/guides/bcd-binary-coded-decimal-explained/

Last updated: 2026-05-21

二进制编码十进制（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 值
- 验证 BCD 数据并识别无效半字节值
- 理解支付系统如何编码数字字段

所有处理都在浏览器中进行——您的数据永远不会离开您的设备。
