要約: 2進化10進数(BCD)は、10進数の各桁を4ビットのニブルとして保存します。BCDの桁加算器では、2進数の和が9を超えるかキャリーが発生したときに+6補正を行います。BCDは10進の桁を正確に保てますが、有限精度の除算やスケーリングには明確な丸め規則が必要です。
2進数と10進数では、位取りを構成する素因数が異なります。そのため、0.1を含む多くの有限小数は、2進数では有限桁で表せません。2進化10進数は10進の各桁を直接保存します。最初の2進小数化による表現誤差は避けられますが、形式に収まらない桁数の結果を扱うには、スケール、精度、丸め規則をあらかじめ定めなければなりません。
2進化10進数(BCD)とは
BCDでは、0から9までの各10進数字を、それぞれ固有の4ビット2進コードで表します。4ビットなら16通りを符号化できますが、BCDが使うのは最初の10通りだけです。
| 10進数 | BCDニブル |
|---|---|
| 0 | 0000 |
| 1 | 0001 |
| 2 | 0010 |
| 3 | 0011 |
| 4 | 0100 |
| 5 | 0101 |
| 6 | 0110 |
| 7 | 0111 |
| 8 | 1000 |
| 9 | 1001 |
残りの6パターン(1010から1111、純粋な2進数では10から15)は、BCDでは無効です。このパターンを出力する回路は、桁がオーバーフローしたか、不正な入力を受け取ったことになります。BCD加算器はこの状態を検出して補正しなければなりません。
複数桁の10進数は、ニブルを連結して符号化します。たとえば327は次のようになります。
純粋な2進表現 と比べると、BCDは12ビットを使うのに対し、2進数は9ビットで済みます。BCDはビットを余分に使う代わりに、桁単位の表現を保ち、10進インターフェースを簡単にします。
パックBCDとアンパックBCD
記憶形式には、代表的なものが2種類あります。
アンパックBCDでは、1バイトに1桁を格納します。下位ニブルが数字を持ち、上位ニブルは0、またはASCII形式のタグ(初期のIBMやx86のBCD命令で使われた0x30)を持ちます。後者なら印字可能なASCII数字をそのまま生成できます。アンパックBCDは記憶領域を半分無駄にしますが、表示処理は単純です。
パックBCDでは、1バイトの各ニブルに1桁ずつ、合計2桁を格納します。327は 0x03 0x27 の2バイトになります。パックBCDはアンパック形式の2倍密で、ハードウェアのBCD加算器やx86の DAA / DAS 命令で使われる形式です。
| 形式 | 数字327 | バイト数 | 密度 |
|---|---|---|---|
| アンパック | 0x03 0x02 0x07 | 3 | 1桁/バイト |
| パック | 0x03 0x27 | 2 | 2桁/バイト |
| 純粋な2進数(16ビット) | 0x01 0x47 | 2 | 全範囲 |
16ビットに収まる値(最大65,535)では、純粋な2進数は密度の面でパックBCDに匹敵しながら、10,000通りではなく65,536通りの値を表現できます。コストが現れるのは、10進数と完全に一致する演算が必要になったときです。
BCDが今も使われる理由
2進数のほうが密なのに、なぜBCDを使うのでしょうか。BCDが残り続ける理由は主に3つあります。
保存した10進桁が正確であること。 10進の0.1には有限桁の2進表現がありません。小数点位置を形式の一部として定めれば、BCDは 0.1 を正確に保存できます。ただし、演算が正確なのは、数学的な結果が指定した桁数とスケールに収まる場合だけです。除算など結果が循環する演算には、やはり丸めが必要です。このトレードオフの2進側については、IEEE 754浮動小数点で解説しています。
表示への変換が簡単なこと。 デジタル時計や電卓は、各BCDニブルを小さなルックアップデコーダーに通して7セグメント表示器を駆動できます。10で割る処理も、繰り返し減算するループも、ソフトウェア変換も必要ありません。この利点を具体化したものが、続編で扱う BCDから7セグメントへのデコーダーです。
10進の業務ルールを扱いやすいこと。 金融や計測では、10進スケールと丸めが指定されることがよくあります。パック10進、COBOLの COMP-3、10進浮動小数点、スケーリングした2進整数はいずれも有効な表現です。桁単位の保存やインターフェースが重要な場合にBCDは便利ですが、規制が常にBCDを要求するわけではありません。
BCDの桁加算:+6補正
BCD加算では、通常の4ビット2進加算器(復習には4ビット・リップルキャリー加算器を参照)を使い、その後に9を超えた結果を直す10進補正段階を置きます。
規則は単純です。2つのBCD桁を4ビット2進数として加算し、次のように処理します。
- 2進和が9以下で、かつキャリーがなければ、その和をそのままBCD結果とします。
- 2進和が9より大きいか、4ビット加算器からキャリーが出た場合は、6を加えます(
0110)。これで補正し、次の桁へキャリーを伝えます。
なぜ6なのでしょうか。BCDは1010から1111までの6パターンを飛ばしているからです。その範囲に入る、またはそれを超える2進和は、BCDの番号付けでは本来の位置よりちょうど6つ先にあります。6を加えると桁が折り返され、10進演算に必要なキャリーが生成されます。
例1:7 + 5
オペランドを符号化し、4ビット2進数として加算します。
結果 1100 は2進数の12で、BCDでは無効なパターンです。+6補正を適用します。
補正後は 0001 0010 です。これは十の位へのキャリーと一の位の2、つまりBCDで12を表します。10進演算の結果と一致します。
例2:8 + 9
4ビット加算器は、キャリーを伴う 0001 を出力します。この 0001 自体は有効なBCD桁に見えますが、キャリーが出たので+6補正が必要です。
最終結果 0001 0111 はBCDの17です。補正せずにキャリーと下位ニブルを連結すると 0001 0001 になり、BCDデコーダーは17ではなく11と解釈します。
「補正が必要」と判定する一般的な条件は次の式です。
ここで は4ビットの2進和です。2つのAND項は無効な6パターン1010から1111をまとめて検出し、 は和が15を超えて折り返した場合を検出します。
1桁BCD加算器のハードウェア
1桁BCD加算器は、4ビット2進加算器2個と小さな補正検出回路で構成できます。
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
最初の加算器が生の2進加算を行い、検出ブロックが補正信号を計算します。2番目の加算器は中間和に0または6を必ず加えます。BCDキャリー出力は補正信号そのものです。6を加える必要があるときは、次の10進桁へ1を伝える必要もあるからです。このブロックを 個デイジーチェーン接続すれば、 桁のBCDリップル加算器になります。構造は2進の全加算器と半加算器の連鎖に似ていますが、桁幅と補正段階が加わります。
2つの加算器ブロックと検出ロジックでこの回路を構成し、結果を桁表示器へ入力できます。入力は有効なBCD桁0~9に限定してください。この補正式は、不正な入力ニブルを一般的に浄化する回路ではありません。
BCD減算
減算では、2進数の2の補数の代わりに9の補数または10の補数を使います。 を計算する手順は次のとおりです。
- まず を用意し、各桁 を に置き換えて9の補数を作ります。
- その結果をBCD加算としてに加えます。
- 最終キャリーが出たら、結果に1を加えます(10の補数補正)。このキャリーは結果が正であることを示します。
- キャリーがなければ結果は負です。もう一度補数を取り、絶対値を求めます。
9の補数は2進数の1の補数に、10の補数は2の補数に対応します。BCD ALUでは、減算側にも同じ補正段階を用意すればよいのです。
BCDの欠点
利点には現実的なコストもあります。
記憶領域のオーバーヘッド。 10進1桁に4ビット必要なので、4バイトで表せるのは最大でも99,999,999です。同じ32ビットの純粋な2進数なら約42.9億まで扱え、範囲は約43倍になります。科学計算や画像処理ではBCDは非効率です。
演算量が増えること。 各桁の加算に2進加算、補正判定、補正処理が必要です。x86の古い10進補正命令の一部は64ビットモードで利用できません。パック10進、10進浮動小数点、スケーリング整数のどれが最適かは、ISA、ワークロード、ライブラリ、必要な意味論に左右されます。
小数をネイティブに扱えないこと。 ここで説明したBCDは整数を符号化します。小数BCDでは小数点位置を規約で固定するか、密パック10進数やIEEE 754-2008の10進浮動小数点へ移行します。後者は3桁ずつをよりコンパクトなコードにします。こうした符号化の歴史は、初期の2進数体系と、10進で数えるか2進で数えるかという長い議論にまでさかのぼります。
BCDが今も活躍する場所
計算そのものが2進であっても、入出力が10進ならBCDは残ります。
- デジタル時計と腕時計。 24時間時計は時・分・秒を6桁のBCDで保存します。カウンターチップが秒を増やし、小さな比較器が60を検出して分へ繰り上げます。同じ仕組みで60分と24時間も処理します。各桁はBCDから7セグメントへのデコーダーを介してLEDを駆動します。
- 電卓。 桁指向の演算は、10進入力と表示を簡単にできます。ただし電卓の内部実装では、2進数などの表現も併用します。
- 産業用操作盤。 サムホイールスイッチはBCDを直接出力し、PLC入力は変換なしでBCDを読み取れます。
- 基幹業務システム。 パック10進をサポートするアーキテクチャや言語は、固定小数点の業務データやCOBOLで今も有用です。
- メーターとPOS端末。 10進表示と指定された丸め規則があると、桁指向の形式が便利です。ただし実装方式はさまざまです。
問題領域が本質的に10進なら、毎回基数変換を行うより、桁を明示的に符号化したほうがよい場合があります。CPU内部が2進であっても、金銭処理で固定小数点整数を使う考え方は、同じ理由に基づいています。
シミュレーターで試す
1桁BCD加算器を作り、補正が働く様子を確認するのがよい練習になります。
- 2つの4ビット入力レジスター、を加算器ブロックへ接続し、生の5ビット和を表示します。
- 、、 のORとして検出ロジックを作ります。
- 検出信号で定数0110をゲートし、2番目の加算器へ入力して最初の和に加えます。
- 補正後の下位ニブルを桁表示器へ、BCDキャリーを別の桁へ入力します。
- 、 を全て試します。表示が2桁で0~18を示し、A~Fの記号が一度も出ないことを確認します。
値の幅を広げる場合は、BCD加算器ブロックをもう1つ連結します。最初のキャリー出力を次のブロックのキャリー入力へ接続する点は、2進リップルチェーンと同じです。
次に読む記事
1桁BCD加算器は、結果を読めて初めて役に立ちます。次の記事のBCDから7セグメントデコーダー:数字から表示へでは、BCD真理値表からセグメント駆動用のブール式を導き、実際の表示器へ配線する方法を示します。先に扱ったデコーダーの基礎を発展させた内容です。2進側の同じ問題を見たい場合は、IEEE 754浮動小数点で、なぜ金融処理が10進表現を選び続けるのかを説明しています。
4ビット2進カウンターのテンプレートを開き、カウント入力をBCD加算器へ接続して、10回目のティックごとに補正が発生する様子を観察してください。+6補正を最も直接的に確認できます。



