2の補数とは?負の数を2進数で表す方法を基本情報の例題で解説
2進数には「−」の記号が無いのに、どうやってマイナスの数を表しているんだろう。
基数変換までは分かったのに、2の補数の問題で手が止まったことはありませんか?
先に答えをお伝えします。2の補数とは、2進数で負の数を表すための決まりで、元の数のビットをすべて反転して1を足すと求められます。
たとえば8ビットで −5 を表すと、5 の 00000101 を反転して 11111010、これに1を足して 11111011 になります。コンピュータは、この形を −5 として扱います。
この記事では、2の補数の考え方から始めて、求め方、10進数への戻し方、表せる値の範囲、引き算とオーバーフローまでを順に説明します。例題はすべてこの記事のために作ったもので、途中式も省かずに書きます。
2の補数の考え方¶
最初に用語を確認しておきます。ビットとは、2進数の1桁のことで、0か1のどちらかが入ります。
8ビットなら、0と1が8個並んだ数です。
コンピュータの中では、数はすべて決まった桁数のビットで扱われます。そこにマイナスの記号を書く場所はないので、ビットの並び方そのもので負の数を表す工夫が必要になります。
一番左のビットで符号を表す¶
2の補数では、一番左のビットが符号を表します。このビットを符号ビットと呼び、0なら0以上の数、1なら負の数です。
ただし、符号ビットを1にするだけで負の数になるわけではありません。−5 は 10000101 ではなく 11111011 です。
ここが最初につまずきやすいところなので、次の節で理由を見ていきます。
足して桁あふれするとゼロになる数¶
2の補数の正体は、「元の数と足すとちょうど桁があふれて0になる数」です。8ビットで考えると、5 と −5 の2の補数を足した結果は次のようになります。
00000101 (5)
+ 11111011 (−5 の2の補数)
----------
1 00000000
9桁目に1があふれますが、8ビットの箱には入らないので捨てられます。残った8ビットは 00000000 で、5 + (−5) = 0 がきちんと成り立っています。
身近なたとえでいえば、車の走行距離計に近い仕組みです。メーターが 0000 のときに1つ戻すと 9999 になるように、2進数でも 00000000 から1を引くと 11111111 になり、これが −1 を表します。
2の補数の求め方¶
求め方は、手順を覚えてしまえば機械的に進められます。基本の方法と、慣れたら使える近道の2つを紹介します。
基本の方法:反転して1を足す¶
基本の手順は2段階です。1段階目で元の数の0と1をすべて入れ替え、2段階目でその結果に1を足します。
0と1を入れ替えることを、ビット反転といいます。
8ビットで −12 を求める流れを、表で追ってみます。
| 手順 | 内容 | ビット列 |
|---|---|---|
| 1 | 12 を8ビットの2進数にする | 00001100 |
| 2 | すべてのビットを反転する | 11110011 |
| 3 | 1を足す | 11110100 |
答えは 11110100 です。最後の1を足すところで繰り上がりが起きることもあるので、筆算で丁寧に計算してください。
検算もしておきましょう。11110100 をふつうの2進数として10進数にすると、128 + 64 + 32 + 16 + 4 = 244 です。
8ビットの場合、2の補数は 256 から元の数を引いた値になるので、256 − 12 = 244 と一致します。
近道:右から最初の1まで残して、その左を反転する¶
慣れてきたら、もっと速い方法も使えます。右端から見ていき、最初に出てくる1まではそのまま残し、それより左のビットだけを反転する方法です。
12 の 00001100 で試すと、右から「100」までがそのまま残ります。残りの左側「00001」を反転して「11110」にすると、11110100 になり、基本の方法と同じ答えです。
試験では、基本の方法で解き、近道で検算するという使い方がおすすめです。2つの方法で答えがそろえば、計算ミスの心配はぐっと減ります。
2の補数を10進数に戻す方法¶
試験では、2の補数で書かれたビット列が10進数でいくつかを問われることもあります。まず符号ビットを見て、0ならそのまま10進数に直し、1なら負の数として次の手順で戻します。
負の数は、もう一度2の補数をとる¶
符号ビットが1のときは、もう一度「反転して1を足す」を行います。出てきた値にマイナスを付ければ、それが答えです。
8ビットの 11110110 で確かめてみます。符号ビットは1なので負の数です。
反転すると 00001001、1を足すと 00001010 になり、これは10進数の 10 です。したがって答えは −10 です。
符号ビットに重みを付けて計算する方法¶
もう1つ、各ビットの重みを足し合わせる方法もあります。8ビットの場合、一番左のビットの重みを +128 ではなく −128 として計算します。
| ビットの位置 | 左端 | 2 | 3 | 4 | 5 | 6 | 7 | 右端 |
|---|---|---|---|---|---|---|---|---|
| 重み | −128 | 64 | 32 | 16 | 8 | 4 | 2 | 1 |
| 11110110 の各ビット | 1 | 1 | 1 | 1 | 0 | 1 | 1 | 0 |
1が立っている位置の重みを足すと、−128 + 64 + 32 + 16 + 4 + 2 = −10 です。先ほどの方法と同じ答えになりました。
この方法は、ビット数が少ない問題で特に速く解けます。4ビットなら、重みは左から −8、4、2、1 です。
nビットで表せる値の範囲¶
2の補数でよく問われるのが、nビットで表せる値の範囲です。結論からいうと、−2の(n−1)乗 から 2の(n−1)乗 − 1 までになります。
正の側が1つ少ないのは、符号ビットが0の組合せの中に0が含まれるからです。0を正の側で1つ使う分、正の数の最大値は負の数の絶対値より1小さくなります。
ビット数ごとの範囲を表にまとめます。
| ビット数 | 最小値 | 最大値 | 表せる値の個数 |
|---|---|---|---|
| 4ビット | −8 | 7 | 16個 |
| 8ビット | −128 | 127 | 256個 |
| 16ビット | −32768 | 32767 | 65536個 |
8ビットの最小値 −128 は 10000000 です。これを反転して1を足しても 10000000 に戻ってしまうので、8ビットでは +128 を表せないことが分かります。
ほかの表し方との比べ方¶
負の数の表し方には、2の補数のほかにも方法があります。符号ビットだけを1にする符号と絶対値表現と、ビットを反転するだけの1の補数です。
4ビットで比べると違いが分かりやすくなります。
| 表し方 | −3 のビット列 | 表せる範囲 | 0の表し方 |
|---|---|---|---|
| 符号と絶対値表現 | 1011 | −7〜7 | 0000 と 1000 の2通り |
| 1の補数 | 1100 | −7〜7 | 0000 と 1111 の2通り |
| 2の補数 | 1101 | −8〜7 | 0000 の1通り |
2の補数は0の表し方が1つしかなく、足し算の回路でそのまま引き算ができます。この扱いやすさから、多くのコンピュータで整数の表現に使われています。
2の補数を使った引き算とオーバーフロー¶
2の補数の便利なところは、引き算を足し算に置き換えられる点です。A − B は、A に B の2の補数を足せば計算できます。
例題1:4ビットで 7 − 3 を計算する¶
まず 3 の2の補数を求めます。3 は 0011、反転すると 1100、1を足して 1101 です。
0111 (7)
+ 1101 (−3)
-------
1 0100
5桁目にあふれた1を捨てると 0100 が残り、10進数の 4 です。7 − 3 = 4 なので、正しく計算できています。
例題2:オーバーフローを見分ける¶
オーバーフローとは、計算結果が表せる範囲を超えてしまうことです。4ビットで 5 + 4 を計算してみます。
0101 (5)
+ 0100 (4)
-------
1001
結果の 1001 は、符号ビットが1なので負の数です。反転して 0110、1を足して 0111 で 7 なので、1001 は −7 を表します。
正しい答えは 9 ですが、4ビットの最大値は 7 なので収まりません。正の数どうしを足したのに結果が負になったことが、オーバーフローの合図です。
見分け方の決まりを表にしておきます。例題1のように桁あふれの1を捨てるのはふつうの計算で、オーバーフローとは別のものです。
| 足す2つの数 | 結果の符号 | 判定 |
|---|---|---|
| 正と正 | 負になった | オーバーフロー |
| 負と負 | 正になった | オーバーフロー |
| 正と負 | どちらでも | オーバーフローは起きない |
正と負を足すと、結果の絶対値は元の2つより大きくならないため、範囲を超えることはありません。符号が同じ2つを足したときだけ、結果の符号を確かめれば十分です。
例題3:ビット数を増やすときの注意¶
4ビットの 1101(−3)を8ビットに広げる問題もあります。このときは、符号ビットと同じ値で左側を埋めます。
1101 の符号ビットは1なので、左に1を4つ足して 11111101 になります。
0で埋めて 00001101 にすると 13 になってしまうので注意してください。この操作を符号拡張といいます。
練習問題で手順を確かめる¶
ここまでの手順を使って、もう2問解いてみましょう。どちらも8ビットの2の補数で考えます。
練習問題1:−20 を8ビットの2の補数で表す¶
まず 20 を8ビットの2進数にします。20 = 16 + 4 なので、00010100 です。
すべてのビットを反転すると 11101011 になります。これに1を足すと、右端が 1 + 1 で繰り上がり、11101100 が答えです。
近道でも確かめておきます。右から最初の1までの「100」を残し、左側の「00010」を反転して「11101」にすると、11101100 になり一致します。256 − 20 = 236 で、11101100 を10進数にした 128 + 64 + 32 + 8 + 4 = 236 とも合っています。
練習問題2:10000001 は10進数でいくつか¶
符号ビットが1なので、負の数です。反転すると 01111110、1を足すと 01111111 になります。
01111111 は 64 + 32 + 16 + 8 + 4 + 2 + 1 = 127 なので、答えは −127 です。重みで計算しても、−128 + 1 = −127 と同じ答えになります。
10000000 が −128、10000001 が −127 と、ビット列を1増やすと値も1増えることが分かります。11111111 が −1 になるのも、この続きとして理解できます。
科目Aと科目Bでの出方と学習の進め方¶
2の補数は、科目Aの基礎理論で、値の求め方や表せる範囲として問われます。計算自体は短いので、手順を覚えれば得点しやすい分野です。
科目Bでも、ビットを扱う擬似言語のプログラムで、2進数の考え方が前提になることがあります。論理積やシフトを使った問題の追い方は、こちらの記事で解説しています。
【関連記事】科目Bのビット演算問題の解き方|論理積とシフトを2進数で追う
科目Bでは、擬似言語のプログラムを1行ずつ読み、変数の値を追う力が中心になります。出題の全体像は、次の記事で初心者向けにまとめています。
【関連記事】基本情報技術者試験の科目Bとは?擬似言語・アルゴリズム対策を初心者向けに解説
基礎理論が試験全体のどこに位置するのかを知りたい人は、親記事で試験内容と勉強法を確かめてください。
【関連記事】基本情報技術者試験とは?試験内容・難易度・勉強法を初心者向けに解説
私たちの Giji Academy の擬似言語の講座では、記法の基礎から、条件分岐・繰返し・配列・アルゴリズム・FE科目B対策までを順に学べます。擬似言語シミュレーターを使えば、プログラムを1行ずつ実行しながら変数の値が変わる様子を確かめられるので、2進数の計算に慣れたら擬似言語の講座で科目Bの演習にも進んでみてください。
まとめ¶
2の補数は、2進数で負の数を表す決まりです。求めるときは、元の数のビットを反転して1を足します。
10進数に戻すときは、符号ビットが1ならもう一度2の補数をとってマイナスを付けるか、左端の重みを負として足し合わせます。nビットで表せる範囲は −2の(n−1)乗 から 2の(n−1)乗 − 1 までで、8ビットなら −128〜127 です。
引き算は2の補数を足すことで計算でき、同じ符号の数を足して結果の符号が変わったらオーバーフローです。
今日の例題を、紙と鉛筆で1問ずつ解き直してみてください。手順が手になじめば、2の補数の問題は確実に点を取れる問題になります。