負数の加算と考える
ここではコンピュータにおける2進数の演算処理のうち、減算を考えてみましょう。
これまでは理論として考えていたので桁数は無限でしたが、この章では実用のメモリの制限を考え有限桁数で考えていきます。
本来はもう少しサイズの領域を使用しますが、ここでは簡略化のため1バイト(8ビット)の整数型を例として解説します。
タイトルにもありますが、CPUでは減算を負数の加算として考えます。
5 - 3
という演算は
5 + (-3)
と変換して計算します。
何故このように考えるかというと、一から減算の仕組みを作るよりも、負数に変換した後に加算の仕組みを用いる方が楽だからです。
負数への変換に際し、-1、-2…といった負の数を定義しなければなりません。
単純に正数に-符号をつけるだけなので数の総数は2倍に増えますが、1変数の確保できる領域は決まっています。
そのため変数の最上位ビットを正負の符号を表す符号ビットとして使用します。
つまり今までの符号なし8ビットでは0~255の256個だった範囲を、符号付8ビットでは-128-127の256個として使用することになります。
例えば10進数1の負数-1は以下のようになります。
1 : 00000001
-1 : 10000001
1の補数と2の補数
負の数は定義できましたが、これだけでは計算できません。
上記の1+(-1)は0になるはずですが計算すると-2になります。
そこで次に考えるべき数Xは
5 - 3 = -2
なので
5 + X = -2
となるような数になります。
素直に考えても足すことで値が減る数は作れないでしょう。
そこで桁あふれという性質を使います。
誤差の話で語られることの多い用語ですが、減算の処理ではその性質を利用して再現しています。
以下は符号なし8ビットの数です。
11111111
00000001
この2数を加算するとどうなるでしょう。
一桁ずつ計算していくとそれぞれ桁上がりし、本来であれば
100000000
となるはずです。
しかしこれは8ビットと大きさが決まっています。
そのため結果は
00000000
となります。
このように制限のある領域からはみ出した部分が消えてしまうことを桁あふれ(オーバーフロー)といいます。
この桁あふれによって桁上がり分を無視するとで加算によって数字を減らすことができます。
先ほどから使っている例でいうと、10進数5と足して2になるような数は
00000101
+ xxxxxxxx
100000010
であり、これを満たすxxxxxxxxは
11111101
となります。
これと引く数である10進数3との関係を見ると、
00000011
ビットを反転し、
11111100
さらに1を足したものとなります。
11111101
ビットを反転させることで、元の数と足すと全ての桁が0と1の和になる為1111…と、全てのビットが1となる数になります。
それに1を足すと10000…とその桁から1桁分桁上がりした数になります。
このうち前者をn進数におけるn-1の補数、後者をnの補数といいます。
2進数の減算においてはその性質から2の補数が使われます。
n-1の補数はnの補数の算出の過程で出てきますが、n進数の減算には使いません。
すなわち、2進数の減算は先頭ビットの符号を確認し、負数ならばデータビットの2の補数をとり、加算を行うという手順になります。