補数とは
ある数に足すとその数の桁からちょうど1桁繰り上がる数
これだとわからないので例を見ていきます
10進数
ある数46に対しての補数は
46に足すとちょうど1桁繰り上がる数なので46は2桁なので
そのちょうど1桁繰り上がると100ですので以下のように計算
100-46 = 54
これが46に対する補数で、その逆に54に対しての補数は46です。
また、この例は10進数を基準にしているので46に対する10の補数という
2進数
2進数の場合はビットを固定してそれを基準に考えます
ビット数を4ビット固定とした場合、そこからちょうど1桁繰り上がと10000の5桁です。
ですので、3に対する補数は以下のように計算する
10000
- 0011
-------
1101
これは2進数を基準としているので2の補数という
補数とコンピューターの負の数の扱い
コンピューターでの負の数の扱い
int型の変数を定義したときほとんどのプログラミング言語は32ビットを確保します。
感覚的に考えると表せる最大値は2^32(4294967296)のように思えます
しかし、これでは負の数も-4294967296まで表すのに32ビット追加で必要になってしまいます。
ですので32ビットを使って正の数も負の数も表すために2^32乗の約半分にした数をそれぞれ表せる正の最大と負の最小にしています。
また、正の最大を超える場合は負の数として扱います
ビットを円状にして考えると最大値を超えると負の値に変わることが視覚的に理解しやすくなります。わかりやすいです。(例として4ビット)
このようにすると先頭のビットを符号として
0が+、1がーを表し残りのビットを整数部と考えることができる
2の補数と負の数
上記の図で表したように0001の負の数は1111です。
これをなぜコンピューターは負の数として定義しているのか
その理由に関係するのが2の補数です。
4ビットを例に考えると
正の数1に対しての補数を求めるとして
補数の定義から4ビットからの一つ桁上がりは10000です。
ですので以下の計算で補数を算出します。
10000
- 0001
-------
1111
+1に対しての補数を求めた結果-1になったのがわかると思います。
これはたまたまでなく2進数の場合、以下のことが言えます。
ある正の数の補数はその数の負の数になり、ある負の数の補数は
その数の正の数になる
なぜそうなるのかについては、2の補数の別の求め方と性質について理解することで納得できます。
2の補数の別の求め方
2の補数について求め方として一般的に以下のことが言われています。
ある数の補数はある数のビットを反転して1を足したものである
先ほどの+1の補数を求めた計算で確かめます。
+1は0001でこのビットを反転 -> 1110
反転したビットに1を足す -> 1111
確かに同じ結果が求まりました。
なぜそうなるかは補数の求め方を以下のように工夫するとわかります。
1111
- 0001
------
1110
+ 1
------
1111
10000は1111+1なので以下のように分解します
すると全桁が1になたものから引くので結果的に
ある数の桁が0なら1-0で1
ある数の桁が1なら1-1で0
これはビットを反転しているのと同じことです。
そして残った+1をすることで結果10000-0001をしている
補数の性質
補数同士を足すと0になる
ビット反転+1をすると1111になったものをまたビット反転+1をすると0001に戻ります
それぞれを足すと
0001+1111 = 10000となり、0になってない
これは+1に対しての補数は10000-0001で求めていました
ですので、0001+(10000-0001)を計算しているのと同じなので余分な10000が残ったままなのです
よって4ビットオーバーしたものを無視することができ、0000=0になります。
2の補数とコンピューターでの演算の関係
まず、コンピューターがどのように計算するのかコンパイル型言語での計算処理で簡単に説明します。
加算処理
int A = 2
int B = 3
int C = A + B
コンパイル
- コンパイル開始
- コンパイラがAの符号部分(+)と整数リテラル(A)に分解
- コンパイラがBの符号部分(+)と整数リテラル(B)に分解
- Aの型がint(符号付き 32 ビット整数)であることを確認
- Bの型がint(符号付き 32 ビット整数)であることを確認
- CPU の命令セット(x86, ARM など)に対応する機械語を生成
(ここで2進数に変換されて加算処理するようなコードを生成) - 生成された命令をバイナリ(2進数)に変換
- 結果をメモリに格納
- コンパイル完了
コード実行
- コンパイルしたコードを実行
- OS がプログラムをメモリにロード
- CPUが機械語を順番に読み取り、その命令によっていメモリを操作して計算を行う
減算処理
int A = 2
int B = 3
int C = B - A
B + (-A)として加算処理に変換します
後の基本手順は加算とほとんど同じです。
ただし、CPU の命令セット(x86, ARM など)に対応する機械語を生成する部分で以下の工程が追加されます。
- -Aを求めるためにAの2の補数を求める
この工程により求めた数とBとで加算処理をする
つまりコンピューターでは減算処理は2の補数を使い加算処理として計算する
このメリットは
- 2の補数はビット反転+1と機械的にもとまるので計算が楽
- 減算処理を加算処理にできることで回路をそれように作らなくてよくなるので
ハードウェアーの設計が簡単になる
まとめ
- 補数とはある数に足すとちょうど一つ桁上りする数
- 2進数の補数を2の補数という
- 2の補数は対象の数の符号が反転したものとなる
- ある数とその2の補数を足すと桁溢れになり、結果が0になる
- ある数の2の補数はその数のビット反転+1
- コンピューターは2の補数を使うことでマイナスの計算を加算処理にする
- 減算処理を加算処理にすることでハードウェアーの設計が簡単