🔢
概念 #CODE #2進数 #16進数 #ASCII #Unicode #UTF-8 #文字コード 📚 CODEコンピュータのからくり

数の体系:2進数・16進数・ASCIIからUnicodeへ

10進数が「指が10本」だからという話から始まり、2進・8進・16進の変換方法、バイト・ASCII・UTF-8まで。なぜコンピュータが16進数を好むかも解説。CODE第2版 Ch.9-13。

なぜ10進数を使うのか

答えは単純:人間の指が10本だから。

数学的に10という数に特別な意味はない。指が8本なら8進数が「自然な数え方」になっていた。

位取り記数法の原理(基数 n の場合):

  各桁 = (0 〜 n-1) の値 × n^(桁の位置)

10進数 342 の意味:
  3 × 10² + 4 × 10¹ + 2 × 10⁰
= 300  +  40  +  2  = 342

この原理はどの基数でも同じ。

2進数(基数2)

使う数字:0 と 1 だけ。

2進数 → 10進数 への変換:

  1 1 0 1 (2進)
  ↑ ↑ ↑ ↑
  8 4 2 1  ← 2の累乗(右から 2⁰, 2¹, 2², 2³)

  1×8 + 1×4 + 0×2 + 1×1 = 13 (10進)

10進数 → 2進数 への変換(繰り返し2で割る):

  42 ÷ 2 = 21 余り 0
  21 ÷ 2 = 10 余り 1
  10 ÷ 2 =  5 余り 0
   5 ÷ 2 =  2 余り 1
   2 ÷ 2 =  1 余り 0
   1 ÷ 2 =  0 余り 1

  余りを下から読む → 101010 (2進) = 42 (10進)

8進数(基数8)

使う数字:0〜7。かつてUnixでファイルパーミッション(chmod 755)に使われた。

2進数 → 8進数 の変換(3ビットずつグループ化):

  110 101 010 (2進)
  ↓   ↓   ↓
   6   5   2  (8進) = 652 (8進)

なぜ3ビット = 1桁かというと、2³ = 8 だから。

16進数(基数16)

使う数字:0〜9 と A〜F。0x プレフィックスで表記。

対応表:
  10進 │ 16進 │ 4ビット2進
  ─────┼─────┼───────────
    0  │  0   │ 0000
    1  │  1   │ 0001
    2  │  2   │ 0010
    3  │  3   │ 0011
    4  │  4   │ 0100
    5  │  5   │ 0101
    6  │  6   │ 0110
    7  │  7   │ 0111
    8  │  8   │ 1000
    9  │  9   │ 1001
   10  │  A   │ 1010
   11  │  B   │ 1011
   12  │  C   │ 1100
   13  │  D   │ 1101
   14  │  E   │ 1110
   15  │  F   │ 1111

なぜコンピュータは16進数を好むか

1バイト = 8ビット = 16進数2桁でぴったり表せる

8ビット 2進数:  1010 1110
            ↓ 4ビットずつ分割
              A      E
            = 0xAE

メリット:
  ・2進数より短く書ける(8文字 → 2文字)
  ・2進数との変換が暗算で一瞬
  ・メモリダンプ・アドレス表記に標準
  ・色コード(#FF5733)、MACアドレス(00:1A:2B:3C:4D:5E)など

バイト(Byte)

なぜ8ビット = 1バイトなのか。

歴史的経緯:
  1950〜60年代、コンピュータは様々なバイトサイズを使用
  (6ビット・7ビット・8ビットなど)

  IBMが1960年代に8ビットを標準化(System/360)

8ビット = 256通り(0〜255)の利点:
  ・ASCII(7ビット)を格納できる
  ・16進数2桁でぴったり表せる
  ・2の累乗ビット(4ビットの倍数)で回路設計が簡単

バイトで表せる範囲:
  符号なし:0 〜 255
  符号あり(2の補数):-128 〜 127

ASCII:英数字の7ビット符号化

American Standard Code for Information Interchange(1963年制定)。

主要なASCIIコード:

  32 (0x20) = スペース
  48 (0x30) = '0'  ← 数字は48〜57
  65 (0x41) = 'A'  ← 大文字は65〜90
  97 (0x61) = 'a'  ← 小文字は97〜122
  10 (0x0A) = 改行(LF)
  13 (0x0D) = 復帰(CR)

設計の巧みさ:
  'A'(65) と 'a'(97) の差 = 32 = 0b00100000
  → ビット5を反転するだけで大文字⇔小文字変換できる

  '0'(48) = 0b00110000
  '9'(57) = 0b00111001
  → 数字文字から下4ビットを取り出すと数値になる
    '5'(53) & 0x0F = 5

Unicode:世界のすべての文字を

ASCII は英語のみ。日本語・中国語・アラビア語・絵文字には対応できない。

Unicodeの設計:
  コードポイント(番号)で文字を識別
  U+0041 = 'A'(Latin Capital Letter A)
  U+3042 = 'あ'(Hiragana Letter A)
  U+1F600 = '😀'(Grinning Face)

現在のUnicode:
  1,114,112 個のコードポイント(U+0000 〜 U+10FFFF)
  うち約15万文字が割り当て済み(2024年時点)

エンコーディング方式:
  UTF-32:すべて4バイト固定。シンプルだがファイルが大きい
  UTF-16:基本面は2バイト、補助面は4バイト
  UTF-8 :可変長(1〜4バイト)。ASCIIと後方互換

UTF-8:可変長エンコーディングの設計

本書第2版で5ページ追加された重要トピック。

UTF-8のエンコーディングルール:

コードポイントの範囲    バイト数  ビットパターン
─────────────────────────────────────────────────
U+0000   〜 U+007F      1バイト  0xxxxxxx
U+0080   〜 U+07FF      2バイト  110xxxxx 10xxxxxx
U+0800   〜 U+FFFF      3バイト  1110xxxx 10xxxxxx 10xxxxxx
U+10000  〜 U+10FFFF    4バイト  11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

例:'あ'(U+3042)を UTF-8 にエンコード

  U+3042 = 0011 0000 0100 0010 (16ビット)
  U+0800〜U+FFFFの範囲 → 3バイト形式

  1110xxxx 10xxxxxx 10xxxxxx に当てはめる:
    1110[0011] 10[000001] 10[000010]
  = 0xE3      0x81      0x82

UTF-8 の設計の巧みさ

1. ASCIIとの互換性
   U+0000〜U+007F は従来のASCIIバイトと同一
   → 既存のASCIIテキストはそのままUTF-8として有効

2. 自己同期性
   先頭バイトは 0xxxxxxx or 11xxxxxx
   継続バイトは 10xxxxxx のみ
   → ストリームの途中から読み始めても文字境界を発見できる

3. バイト順非依存
   UTF-8 はバイト単位のエンコーディングなのでエンディアン問題なし
   (UTF-16/32 はBOMが必要な場合がある)

変換チートシート

2進 → 10進:各桁に2の累乗をかけて合計
  1011(2) = 8+0+2+1 = 11(10)

10進 → 2進:繰り返し2で割り、余りを逆順に並べる
  11 → 5余1 → 2余1 → 1余0 → 0余1 → 1011(2)

2進 → 16進:4ビットずつグループ化
  1010 1111(2) = A F(16) = 0xAF

16進 → 2進:1桁を4ビットに展開
  0x3C = 0011 1100(2)

16進 → 10進:
  0xFF = 15×16 + 15 = 255
  0x100 = 256

よく使う値を暗記:
  0xFF = 255 = 11111111(2)(8ビット最大)
  0x80 = 128 = 10000000(2)(8ビット最上位ビット)
  0x7F = 127(符号付き8ビットの最大値)
  0x0F = 15  = 00001111(2)(下位4ビットのマスク)

出典: https://www.amazon.co.jp/dp/4296080245