負の数の表現——2の補数とマイナスの取り扱い
問題:0と1だけでマイナスをどう表す?
これまで学んできたビットとバイトは、すべて「0以上の整数(正の数)」を表すものでした。しかし現実の計算では、気温、残高、座標など、マイナスの値 が欠かせません。
コンピュータは0と1しか扱えません。では「-1」や「-100」をどうやって表現するのでしょうか?
いくつかのアイデアが考えられますが、現代のコンピュータはほぼすべて 2の補数(Two’s complement) という方式を採用しています。その理由も含めて、順を追って説明します。
アイデア1:符号ビットを使う
最もシンプルな発想は「最上位ビット(左端のビット)を符号のフラグにする」ことです。
0 で始まる → 正の数
1 で始まる → 負の数
例えば8ビットで:
0 0001010 → +10
1 0001010 → -10
この方式を 符号・絶対値表現 と言います。
しかしこの方法には問題があります。
問題1:ゼロが2種類できる
0 0000000 → +0
1 0000000 → -0
「+0」と「-0」は同じ値のはずなのに、2通りの表現ができてしまいます。
問題2:足し算の回路が複雑になる
符号を判断してから計算を切り替える必要があり、回路設計が難しくなります。
アイデア2:2の補数方式
現代のコンピュータが採用している方式が 2の補数(Two’s complement) です。
この方式では、前回学んだ「オーバーフローで0に戻る」性質をうまく利用します。
核となるアイデア:-1 は「足すと0になる数」
-1の定義は「1を足したらゼロになる数」です。
1 + (-1) = 0
8ビットの世界で「足すと 00000000(= 0)になる数」を探してみましょう。
0 0 0 0 0 0 0 1 (= 1)
+ ? ? ? ? ? ? ? ? (= -1 のビット表現)
------------------
0 0 0 0 0 0 0 0 (= 0)← オーバーフローして0に戻る
計算すると:
0 0 0 0 0 0 0 1
+ 1 1 1 1 1 1 1 1
------------------
1 0 0 0 0 0 0 0 0 ← 9ビット目があふれて 0 0 0 0 0 0 0 0
つまり 11111111 = -1 です!
2の補数の作り方
「ある数のマイナス」を求める手順はシンプルです。
手順1:全ビットを反転する(ビット反転 / NOT演算)
手順2:1を足す
例:-10(マイナス10)を求める
まず 10 を2進数で書きます:
10 = 0 0 0 0 1 0 1 0
ステップ1:ビットを全部反転する(0→1、1→0)
0 0 0 0 1 0 1 0
↓
1 1 1 1 0 1 0 1
ステップ2:1を足す
1 1 1 1 0 1 0 1
+ 0 0 0 0 0 0 0 1
------------------
1 1 1 1 0 1 1 0
結果:11110110 = -10 の2の補数表現
確認してみましょう。10 + (-10) = 0 になるはず:
0 0 0 0 1 0 1 0 (= 10)
+ 1 1 1 1 0 1 1 0 (= -10)
------------------
1 0 0 0 0 0 0 0 0 → あふれて 00000000 = 0 ✓
2の補数がなぜ便利なのか
2の補数の最大の利点は 「足し算の回路をそのまま使える」 ことです。
- 符号付きの足し算も、符号なしの足し算も、同じ回路でOK
- 「+と−を判別してから計算を分ける」必要がない
- ハードウェアが単純で速くなる
例:5 + (-3) = 2 を2の補数で計算
-3 の2の補数表現:
3 = 00000011
反転 → 11111100
+1 → 11111101 ← これが -3
0 0 0 0 0 1 0 1 (= 5)
+ 1 1 1 1 1 1 0 1 (= -3)
------------------
1 0 0 0 0 0 1 0 → あふれを除くと 00000010 = 2 ✓
8ビット符号付き整数の範囲
2の補数方式を使うと、8ビットで表せる値の範囲は次のようになります。
最小値:10000000 = -128
-1:11111111 = -1
0:00000000 = 0
+1:00000001 = +1
最大値:01111111 = +127
| ビットパターン | 符号付き(signed) | 符号なし(unsigned) |
|---|---|---|
| 00000000 | 0 | 0 |
| 00000001 | +1 | 1 |
| 01111111 | +127 | 127 |
| 10000000 | -128 | 128 |
| 10000001 | -127 | 129 |
| 11111110 | -2 | 254 |
| 11111111 | -1 | 255 |
同じビットパターンでも、符号付き(signed)として読むか、符号なし(unsigned)として読むか で全く異なる値になることがわかります。
signed と unsigned の違い
プログラミング言語では、整数型を宣言するとき「符号付き(signed)か符号なし(unsigned)か」を指定できることがあります。
符号なし整数(unsigned)
- 0以上の正の整数だけを扱う
- 8ビットなら 0〜255
- メモリアドレス、色のRGB値など負にならない値に使う
符号付き整数(signed)
- 負の数も扱える
- 8ビットなら -128〜127
- 温度、座標の差分、カウントダウンなど負になりうる値に使う
ビット数ごとの範囲(2の補数方式):
8ビット signed:-128 〜 127
8ビット unsigned:0 〜 255
16ビット signed:-32,768 〜 32,767
16ビット unsigned:0 〜 65,535
32ビット signed:約 -21億 〜 +21億
32ビット unsigned:0 〜 約 43億
64ビット signed:約 -922京 〜 +922京
64ビット unsigned:0 〜 約 1844京
具体的な値で確認する
-1 はなぜ 11111111 なのか
1 の2進数:00000001
ビット反転:11111110
+1: 11111111
-1 = 11111111 ✓
確認:00000001 + 11111111
= 100000000 → 8ビットに収まらず 00000000 = 0 ✓
-128 はなぜ 10000000 なのか
128 の2進数:10000000
ビット反転:01111111
+1: 10000000 ← もとに戻った!
-128 = 10000000 ✓
この「反転して+1したらもとと同じになる」という現象が、-128 が 8ビット符号付き整数の最小値である理由です。-128 に対応する +128 は8ビット符号付きの範囲(-128〜127)に入らないため、対称ではありません。
まとめ
- 符号ビットだけではゼロが2種類できるなどの問題がある
- 2の補数方式:ビット反転して1を足すとマイナスの数が得られる
- -1 = 11111111 は「足すと0になる(オーバーフローして0に戻る)」から
- 2の補数の最大の利点:加算回路をそのまま使えてハードウェアが単純になる
- 8ビット符号付き整数の範囲は -128〜127(符号なしは 0〜255)
- 同じビットパターンでも signed / unsigned で解釈が異なる
これでシリーズ「バイナリデータ入門」の基礎編は完了です。ビットとバイトから始まり、2進数・16進数の表現、オーバーフロー、そして負の数の表現まで、コンピュータがデータを扱う根本的な仕組みを学びました。