DIVE

Part 1: 物理・電子・論理回路

第 1 章

0と1の世界 — すべては電圧の高低から始まった

「コンピュータは0と1しか分からない」——この言葉を聞いたことがあるだろう。だが、なぜ0と1なのか、その答えを正確に言える人は意外と少ない。

今から80年前、コンピュータは存在しなかった。あったのは、ただの電気回路だった。


電圧こそが「0」と「1」の正体

コンピュータが「0と1しか扱えない」というのは比喩ではなく、物理現象の直接の写像だ。

現代のCPU内部では、電圧が約0V付近なら 0、約1.0〜3.3V付近なら 1 として扱われる。これを論理レベル(Logic Level)と呼ぶ。

Vlow≈0V⇒論理0V_{low} \approx 0\text{V} \Rightarrow \text{論理} 0 Vhigh≈1.0–3.3V⇒論理1V_{high} \approx 1.0\text{–}3.3\text{V} \Rightarrow \text{論理} 1

なぜ2値なのか?答えはシンプルだ。電圧は連続的な値を取るが、「高い/低い」という二値判定ならノイズに強い。0.3Vのノイズが乗っても、1.0V閾値を超えない限り誤動作しない。三値・四値でも理論上は可能だが、ノイズ耐性と回路の複雑さのトレードオフで、人類は二値に落ち着いた。


トランジスタ — 電気で電気を制御する革命

二値信号を計算に使うには、信号を「スイッチング」する部品が必要だ。それがトランジスタだ。

1947年、ベル研究所のショックレー、バーディーン、ブラッテンが世界初のトランジスタを発明した。コンピュータ史上、最も重要な瞬間の一つだ。

          コレクタ (C)
             |
             |
ベース (B) ──[N P N]──
             |
          エミッタ (E)

NPN型トランジスタの動作原理はシンプルだ:

小さな電流で大きな電流を制御できる。これがトランジスタの本質だ。

現代のCPUには、このトランジスタが数百億個集積されている。台湾のTSMCが製造する3nmプロセスでは、髪の毛の太さ(約70,000nm)の中に2万個以上のトランジスタが並ぶ。


ブール代数 — 数学が回路になる瞬間

電気スイッチを組み合わせると、数学的な論理演算が実現できる。これを体系化したのがブール代数(Boolean Algebra)だ。1854年、ジョージ・ブールが提唱した。

基本は3つの演算だ:

演算記号意味
ANDA⋅BA \cdot BAかつB(両方1なら1)
ORA+BA + BAまたはB(どちらか1なら1)
NOTA‾\overline{A}Aの否定

これら3つを組み合わせることで、任意の論理演算が実現できる(関数完全性)。

半加算器 — 1ビットの足し算

最も基本的な計算回路として「半加算器(Half Adder)」を例に取ろう。 1ビット同士の加算 A+BA + B を計算すると:

S=A⊕B(Sum: 桁の和)S = A \oplus B \quad \text{(Sum: 桁の和)} C=A⋅B(Carry: 繰り上がり)C = A \cdot B \quad \text{(Carry: 繰り上がり)}

⊕\oplus はXOR(排他的論理和)を表す。真理値表で確認しよう:

ABS (Sum)C (Carry)
0000
0110
1010
1101

1 + 1 = 10(二進数)。10進数で言えば「2」だ。これが1ビット加算の全てだ。


二進数 — 重みの体系

人間が10進数(0〜9)を使うのは「指が10本だから」という説がある。コンピュータが2進数を使うのは「電圧の状態が2つだから」だ。

10進数の仕組みを思い出してほしい:

423=4×102+2×101+3×100423 = 4 \times 10^2 + 2 \times 10^1 + 3 \times 10^0

二進数も同じ原理で、基数が2になるだけだ:

10112=1×23+0×22+1×21+1×20=8+0+2+1=11101011_2 = 1 \times 2^3 + 0 \times 2^2 + 1 \times 2^1 + 1 \times 2^0 = 8 + 0 + 2 + 1 = 11_{10}

Pythonで確認する

# 10進数 → 2進数
n = 42
binary = bin(n)
print(binary)        # '0b101010'
print(binary[2:])    # '101010'  (プレフィックス除去)

# 2進数 → 10進数
b = '101010'
decimal = int(b, 2)
print(decimal)       # 42

# ビット演算で確認
print(42 == (32 + 8 + 2))   # True
print(42 == (1<<5 | 1<<3 | 1<<1))  # True

8ビットで表現できる範囲

符号なし8ビット整数(uint8)が表現できる範囲は:

0≤n≤28−1=2550 \leq n \leq 2^8 - 1 = 255

符号あり8ビット整数(2の補数表現)では:

−27≤n≤27−1  ⟹  −128≤n≤127-2^7 \leq n \leq 2^7 - 1 \implies -128 \leq n \leq 127

「2の補数」については第2章で詳しく扱う。


論理ゲート — トランジスタの組み合わせ

ブール代数の演算は、トランジスタを特定のパターンで接続することで実現する。この「演算を行う最小回路ブロック」を論理ゲート(Logic Gate)と呼ぶ。

AND ゲート:
  A ──┐
      [AND]── 出力
  B ──┘

NAND ゲート(ANDの否定):
  A ──┐
      [NAND]── 出力
  B ──┘

面白い事実がある:NANDゲートだけあれば、AND・OR・NOT・XORを全て構成できる(NANDの汎用性)。実際の集積回路設計でも、論理合成ツールは最終的にNANDとNORの組み合わせへ変換することが多い。

/* Cで論理演算をビット演算で表現 */
#include <stdio.h>
#include <stdint.h>

int main(void) {
    uint8_t a = 0b10110100;  /* 180 */
    uint8_t b = 0b01101011;  /* 107 */

    printf("AND : %08b (%d)\n", a & b,  a & b);   /* 論理積 */
    printf("OR  : %08b (%d)\n", a | b,  a | b);   /* 論理和 */
    printf("XOR : %08b (%d)\n", a ^ b,  a ^ b);   /* 排他的論理和 */
    printf("NOT : %08b (%d)\n", ~a & 0xFF, ~a & 0xFF); /* 論理否定 */
    printf("SHL : %08b (%d)\n", a << 1, a << 1);  /* 左シフト = ×2 */
    printf("SHR : %08b (%d)\n", a >> 1, a >> 1);  /* 右シフト = ÷2 */

    return 0;
}

実行結果:

AND : 00100000 (32)
OR  : 11111111 (255)
XOR : 11011111 (223)
NOT : 01001011 (75)
SHL : 01101000 (104)
SHR : 01011010 (90)

左シフト1ビットが「×2」になるのは、各桁の重み(2n2^n)が一段上がるからだ。これは10進数で「0を付け足すと×10になる」のと同じ原理だ。


まとめ — 「おまじない」の消し方

この章で分かったことを整理しよう:

  1. 「0と1」の正体は電圧の高低:物理現象が直接、論理値になっている
  2. トランジスタがスイッチ:電気で電気を制御し、論理演算を実現する
  3. ブール代数が橋渡し:数学的な論理が回路設計の言語になる
  4. 二進数は電圧の数学:基数2の位取り記数法が、電気回路と自然に対応する

「なぜビット演算は速いのか?」「なぜintは-2147483648〜2147483647なのか?」——これらの問いに答える下地が整った。

次章では、この二進数表現を使ってクロックとレジスタがどのように「時間」と「記憶」を生み出すかを見ていく。


「すべての複雑さは、単純さの組み合わせから生まれる。コンピュータが神秘的に見えるのは、単純なものが10億回繰り返されているからに過ぎない。」