← ブログに戻る記数法

BCD(2進化10進数):基礎を解説

BCDは各10進数字を4ビットで符号化します。パック形式とアンパック形式、+6補正、10進スケーリング、実用上のトレードオフを学びます。

DennyDenny
読了まで約11分
Chalkboard feature illustration for “BCD(2進化10進数):基礎を解説”

要約: 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ニブル
00000
10001
20010
30011
40100
50101
60110
70111
81000
91001

残りの6パターン(1010から1111、純粋な2進数では10から15)は、BCDでは無効です。このパターンを出力する回路は、桁がオーバーフローしたか、不正な入力を受け取ったことになります。BCD加算器はこの状態を検出して補正しなければなりません。

複数桁の10進数は、ニブルを連結して符号化します。たとえば327は次のようになります。

32710  =  0011⏟3  0010⏟2  0111⏟7327_{10} \;=\; \underbrace{0011}_{3}\;\underbrace{0010}_{2}\;\underbrace{0111}_{7}

純粋な2進表現 32710=1010001112327_{10}=101000111_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 0x0731桁/バイト
パック0x03 0x2722桁/バイト
純粋な2進数(16ビット)0x01 0x472全範囲

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進数として加算します。

7=01115=010112=1100\begin{aligned} 7 &= 0111 \\ 5 &= 0101 \\ \hline 12 &= 1100 \end{aligned}

結果 1100 は2進数の12で、BCDでは無効なパターンです。+6補正を適用します。

1100  (12)+  0110  (6)1 0010\begin{aligned} 1100 \;(12) \\ +\;0110 \;(6) \\ \hline 1\,0010 \end{aligned}

補正後は 0001 0010 です。これは十の位へのキャリーと一の位の2、つまりBCDで12を表します。10進演算の結果と一致します。

例2:8 + 9

8=10009=100117=1 0001\begin{aligned} 8 &= 1000 \\ 9 &= 1001 \\ \hline 17 &= 1\,0001 \end{aligned}

4ビット加算器は、キャリーを伴う 0001 を出力します。この 0001 自体は有効なBCD桁に見えますが、キャリーが出たので+6補正が必要です。

0001+  01100111\begin{aligned} 0001 \\ +\;0110 \\ \hline 0111 \end{aligned}

最終結果 0001 0111 はBCDの17です。補正せずにキャリーと下位ニブルを連結すると 0001 0001 になり、BCDデコーダーは17ではなく11と解釈します。

「補正が必要」と判定する一般的な条件は次の式です。

adjust  =  Cout  ∨  (S3∧S2)  ∨  (S3∧S1)\text{adjust} \;=\; C_{\text{out}} \;\lor\; (S_3 \land S_2) \;\lor\; (S_3 \land S_1)

ここで S3S2S1S0S_3 S_2 S_1 S_0 は4ビットの2進和です。2つのAND項は無効な6パターン1010から1111をまとめて検出し、CoutC_{\text{out}} は和が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を伝える必要もあるからです。このブロックを nn 個デイジーチェーン接続すれば、nn 桁のBCDリップル加算器になります。構造は2進の全加算器と半加算器の連鎖に似ていますが、桁幅と補正段階が加わります。

2つの加算器ブロックと検出ロジックでこの回路を構成し、結果を桁表示器へ入力できます。入力は有効なBCD桁0~9に限定してください。この補正式は、不正な入力ニブルを一般的に浄化する回路ではありません。

BCD減算

減算では、2進数の2の補数の代わりに9の補数または10の補数を使います。A−BA - B を計算する手順は次のとおりです。

  1. まず BB を用意し、各桁 dd を 9−d9 - d に置き換えて9の補数を作ります。
  2. その結果をBCD加算としてAAに加えます。
  3. 最終キャリーが出たら、結果に1を加えます(10の補数補正)。このキャリーは結果が正であることを示します。
  4. キャリーがなければ結果は負です。もう一度補数を取り、絶対値を求めます。

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加算器を作り、補正が働く様子を確認するのがよい練習になります。

  1. 2つの4ビット入力レジスターAA、BBを加算器ブロックへ接続し、生の5ビット和を表示します。
  2. CoutC_{\text{out}}、(S3∧S2)(S_3 \land S_2)、(S3∧S1)(S_3 \land S_1) のORとして検出ロジックを作ります。
  3. 検出信号で定数0110をゲートし、2番目の加算器へ入力して最初の和に加えます。
  4. 補正後の下位ニブルを桁表示器へ、BCDキャリーを別の桁へ入力します。
  5. A=0..9A = 0..9、B=0..9B = 0..9 を全て試します。表示が2桁で0~18を示し、A~Fの記号が一度も出ないことを確認します。

値の幅を広げる場合は、BCD加算器ブロックをもう1つ連結します。最初のキャリー出力を次のブロックのキャリー入力へ接続する点は、2進リップルチェーンと同じです。

次に読む記事

1桁BCD加算器は、結果を読めて初めて役に立ちます。次の記事のBCDから7セグメントデコーダー:数字から表示へでは、BCD真理値表からセグメント駆動用のブール式を導き、実際の表示器へ配線する方法を示します。先に扱ったデコーダーの基礎を発展させた内容です。2進側の同じ問題を見たい場合は、IEEE 754浮動小数点で、なぜ金融処理が10進表現を選び続けるのかを説明しています。

4ビット2進カウンターのテンプレートを開き、カウント入力をBCD加算器へ接続して、10回目のティックごとに補正が発生する様子を観察してください。+6補正を最も直接的に確認できます。

アイデアを実際に動く回路にしましょう。