負の数のあらわし方
単純に「負の数を表せればいい」という考え方なら、やり方は様々です。最も単純なところでは、「先頭のビットは符号にするね」と決めてしまう方法があります。
前提として先頭の1ビットが0なら正の数、1なら負の数にする。そこで、例えば10進数の「3」を2進数の8ビットで表すと...
正の数 00000011 (3)
負の数 10000011 (-3)
ところが、これだと「足し算だけで引き算も済ませる」ことはできません。
00000011 + 10000011 = 10000110
⇒3と-3を足したら0にならないといけないはずなのにそうなっていない!
補数
そこで出てくるのが補数という表現方法です。
補数とは言葉の通り「補う数」という意味。補数の種類には「その桁数での最大値を補う数」「次の桁に繰り上がるために補う数」という2つの補数が存在します。
…と書いただけじゃよくわかんないと思うので、10進数の数字を例に実際の数字を見てみましょう。
例 :123 (10進数で3桁の数字があったとします。)
【9の補数】
999 ➡ 999-123=876←これを"9の補数"と呼びます。
↑(3桁の最大値)
⇒いくつ足せば__999__(最大値)になるかというと...876!
つまり876という数値を補ってやれば最大値が得られます。
【10の補数】
1000 ➡ 1000-123=877←これを"10の補数"と呼びます。
↑(次の桁に繰り上がる数)
⇒いくつ足せば__1000__(桁上り)になるかというと...877!
つまり877という数値を補ってやれば桁上りの数が得られます。
このような10進数でいうところの「9の補数」「10の補数」と同じものが、2進数にもあるわけです。2進数では、「1の補数」「2の補数」という2つの補数を使います。
例 :0011 (2進数で4桁の数字があったとします)
【1の補数】
1111 ➡ 1111-0011=1100←これを"1の補数"と呼びます。
↑(4桁の最大値)
⇒いくつ足せば__1111__(桁上り)になるかというと...1100!
つまり1100という数値を補ってやれば最大値が得られます。
【2の補数】
10000 ➡ 10000-0011=1101←これを"2の補数"と呼びます。
↑(次の桁に繰り上がる数)
⇒いくつ足せば__10000__(桁上り)になるかというと...1101!
つまり1101という数値を補ってやれば桁上りの数が得られます。
この2の補数を使ってある計算をしてみますとですね...。
00000011 + 11111101 = 100000000←桁あふれは無視
3 + (-3) = (0)
8ビットの2進数00000011(3)と、その数の2の補数11111101(-3)を足し算すると桁上りした数になります。この時、8ビット目からあふれ出てしまった9ビット目を無視して8ビット部分だけ見れば全部0になります。
このように、ある数値に対する2の補数表現は、そのままその数値の負の値として使えるというわけなのです。このことから、コンピュータは負の数をあらわすのに2の補数を使います。2の補数は、次のようにすると簡単に求めることができます。
00000011 ⤵ 全てのビットを反転させる(0と1を入れ替える) (3)
11111100 ⤵ それに1を加算すると...
11111101 ➡__2の補数__ (-3)
引き算の流れ
それでは実際に例を用いて、引き算の流れを見てみることにしましょう。ここでは「5-3」という式の場合どうなるかを見てみます。
0101(5) - 0011(3) ⤵
0101(5) + (-0011) ⤵ これはこのような足し算で表せるので
0101(5) + 1101(-3) ⬅2の補数表現を使ってこう書くことができる
つまりコンピュータはこのような足し算を行って...
0101(5) + 1101(-3) = 10010(2)
「2」という答えを得るわけです。
ちなみに、2の補数を用いて負の数をあらわす場合も、1ビット目は符号として扱うことができます。正の数と負の数は、互いに2の補数表現となる関係にあります。
0000(0)
1111(-1) ⇔ 0001(1)
1110(-2) ⇔ 0010(2)
1011(-5) ⇔ 0101(5)
1010(-6) ⇔ 0110(6)
1001(-7) ⇔ 0111(7)
1000(-8)