2の補数~C言語で負数のしくみを確かめる~【GitHub対応】

コンピュータのしくみ
OSくん
OSくん

2026年10月18日に全面リライトしました
2023年7月に公開した記事を、「つくって学ぶ」の趣旨に合わせて書き直した。C言語のプログラムで、負数を表すしくみを実際に確認できる内容になっている。

はじめに

コンピューターは、0と1だけで情報を表す。では、マイナスの数は、どのように表すのだろうか。その答えが「2の補数」である。

今回は、補数の考え方を整理してから、C言語のプログラムで、4ビットや8ビットの数を実際に表示して確かめよう。

4ビットで表せる数

4ビットでは、2⁴ = 16通りの組み合わせがある。これを、0以上の2進数に対応させると、0000〜1111(0〜15)を表せる。

しかし、これでは負数を表せない。負数をどう表すかは、コンピュータの設計者が決める。
かつては1の補数が使われていたが、現在の多くのコンピュータは、2の補数を使っている。

補数とは

補数(ほすう)とは、与えられた数を、ある決められた数から引いて得られる数のことである。

まず、10進数で考えてみよう。

  • 10の補数:足すと桁が上がる最小の数(10)になる。「7」の補数は「3」(7 + 3 = 10)
  • 9の補数:足しても桁が上がらない最大の数(9)になる。「7」の補数は「2」(7 + 2 = 9)

これを、nビットの2進数に当てはめる。

  • 2の補数:2ⁿ から元の数を引いた数(4ビットなら、10000 − 元の数)
  • 1の補数:2ⁿ − 1 から元の数を引いた数(4ビットなら、1111 − 元の数。ビットを反転した数と同じ)

2の補数は、「ビットを反転して、1を足す」ことでも求められる。たとえば「3」の場合、次のようになる。

0011(3) → 反転 → 1100 → 1を足す → 1101

1101 は、10進数で13であり、16 − 3 と一致している。この 1101 が、4ビットの「−3」を表す。

2の補数を使う最大のメリットは、マイナス記号を使わずに、負数を表現できる点である。

符号ビット

負数を2の補数で表すと、表現できる値の範囲が変わる。一番左のビットが符号ビットになり、1のときは負数を表す。

範囲4ビットの例
正整数だけ0 〜 2ⁿ − 10 〜 15
2の補数−2ⁿ⁻¹ 〜 +2ⁿ⁻¹ − 1−8 〜 +7

体験1:C言語で4ビットの全パターンを表示する

−8〜+7を、4ビットの2進数で表示しよう。下記のコードをbits4.c で保存して実行しよう。

#include <stdio.h>

int main(void) {
    printf("値 4ビット 16+値\n");
    for (int v = -8; v <= 7; v++) {
        unsigned bits = (unsigned)v & 0xF;   /* 下位4ビットだけ取り出す */
        printf("%2d ", v);
        for (int i = 3; i >= 0; i--) {
            putchar(((bits >> i) & 1) ? '1' : '0');
        }
        if (v < 0) {
            printf("   %2d", 16 + v);
        }
        putchar('\n');
    }
    return 0;
}
gcc bits4.c -o bits4
./bits4

実行結果の一部は、次のようになる。

値 4ビット 16+値
-8 1000    8
 ...
-3 1101   13
-2 1110   14
-1 1111   15
 0 0000
 1 0001
 ...
 7 0111

確認するポイントは、次の3つである。

  • 負数は、すべて左端のビット(符号ビット)が1になる
  • 負数の2進数を、普通の数として読むと、「16 + 値」と同じ数になる(−3 → 13)
  • 表せる範囲は、−8〜+7である

体験2:範囲をはみ出すとどうなるか?C言語で試してみよう

次は、8ビットの整数で、最大値に1を足してみよう。下記のコードをoverflow8.c で保存して実行しよう。

#include <stdio.h>
#include <stdint.h>

int main(void) {
    printf("8ビットの範囲: %d 〜 %d\n", INT8_MIN, INT8_MAX);

    int8_t x = INT8_MAX;
    printf("最大値      : %d\n", x);

    x = (int8_t)(x + 1);
    printf("1を足すと   : %d\n", x);
    return 0;
}
gcc overflow8.c -o overflow8
./overflow8

実行結果は、次のようになる。

8ビットの範囲: -128 〜 127
最大値      : 127
1を足すと   : -128

8ビットで表せる範囲は、−128〜+127である(n = 8 を、先ほどの式に当てはめた値)。
最大値の127(01111111)に1を足すと、10000000 になり、符号ビットが1になるため、−128になる。
このように、8ビットで表せる範囲を超えると、意図した値とは異なる値になることがある。
今回のプログラムでは、gccを使うと −128 が表示される。

まとめ

  • 補数とは、ある決められた数から、元の数を引いた数である
  • 現在の多くのコンピューターは、負数を2の補数で表す
  • 2の補数は、「ビットを反転して、1を足す」ことでも求められる
  • 負数は符号ビットが1になり、nビットで −2ⁿ⁻¹ 〜 +2ⁿ⁻¹ − 1 を表せる
  • 範囲をはみ出すと、符号が逆転した値になる

次に読む

基本情報技術者試験の問題を、Pythonで確かめてみよう。
→ 基本情報技術者試験問題に挑戦しよう!(2の補数)~Pythonでつくって確かめる~【GitHub対応】

更新履歴

  • 2026年10月18日:全面リライト(手を動かす内容に刷新)
  • 2023年7月6日:初版公開

コメント

タイトルとURLをコピーしました