補数を使った減算
コンピュータで整数の減算を行う方法で現在最も普及しているのは、加算回路と二の補数を使う物でしょう。
IBMのSYSTEM/360で採用された事が普及のきっかけになったとされています。
減算回路の文脈で二の補数が出てきたのは、ノイマン先生の "First Draft of a Report on the EDVAC" が最初じゃないかと思います。
http://2908.work/Okiba/vnedvac.pdf
(8.0 CIRCUITS FOR THE ARITHMETICAL OPERATIONS)
実際に動作したのはEDSACの方が先ですが。
定義
b進法において、自然数 a を表現するのに必要な最小の桁数を n としたとき、
bn-a を「b 進法における a に対する基数の補数(b の補数)」
bn-a-1 を「b 進法における a に対する減基数の補数(b - 1 の補数)」
という。
つまり、補数とはある数に加えると桁上がりする数の最小値や、桁上がりしない数の最大値です。
桁上がりがカギですから、何進数で考えるかが重要です。
二進数の補数は一の補数と二の補数があります。
同様に、三進数には三の補数と二の補数があります。
以下この記事では、単に二の補数と言った場合、二進数の二の補数を言います。
二の補数とは、ある数を二進数で表現した時の、ある数に加えた時桁上がりする最小の数です。
桁上がりなので、何桁の数についてなのか考える必要があります。例えば8桁の二進数で1101の二の補数の場合、8桁の0000 1101について考えます。桁上がりしたら1 0000 0000です(0は8個)。
この場合二の補数は 1 0000 0000-0000 1101=1111 0011 です。
求め方
二の補数の場合、各ビットを反転して1を足す、という便利な方法があります。
この求め方を「二の補数の定義」として紹介しているページがありますが、厳密に言うと定義ではありません。これは「コンピュータは加算しか出来ない」と説明してしまったから補数も加算で求めないと辻褄が合わないからでしょう。
しかし、1を引いてからビット反転しても同じ結果になります。何より補数はその定義から減算である事は明らかです。
ではなぜ反転して1を足す… 言い換えて、
なぜ反転すると 二の補数-1 が求められるのでしょうか?
例として4桁の二進数1001について考えてみましょう。
まず、桁上がりした数の最小は5桁の10000です。2の補数の定義から、1001に足した時に10000になる数を求めればいいのですが、このままだと普通に減算が必要です。
そこで10000から1を引いた1111で考えてみましょう。この時に引いた1は最後に足してあげます。
1111-1001ですから各桁は 1-1 か 1-0 で、上の桁からの借りが発生しません。という事は下の桁への貸しもありません。各桁ごとに計算すれば答えを求めることが出来ます。
各桁は必ず1から引くので引かれる数が0の場合は省略します。
引かれる数 | 引く数 | → | 結果 |
---|---|---|---|
1 | 0 | 1 | |
1 | 1 | 0 |
ほら、「引く数」と「結果」が反転の関係になってるでしょ?
ということで1001を反転して0110です、そして、反転する前に引いた1を足して戻してあげれば2の補数です。
4桁の二進数1001の二の補数は
答え:0111
です。
法n
4桁の0000は反転して1を足すと10000です。桁あふれは無視すると解説したページもありますが、4桁の二進数で表現できる数は16種類、法16のもとで10000と0000は合同であるから同じ、と考える事が出来ます(16は十進)。
だとすると、Aの補数は Aに足して0になる数 と言い換える事が出来ます。まさに Aの補数 = 0-A = -A です。
つまり、二進数で二の補数を取るというのは、符号を反転する操作です。
符号反転だから加算<->減算は二の補数を利用して相互に変換可能です。
そして、補数はお互いが補数の関係です。Aの補数がBなら、Bの補数は必ずAです。