ASCII の優雅さ:小文字が大文字のすぐ後に続かない理由
ASCII 表を最初に見たとき、興味深い隙間が現れます。大文字の Z(10 進数で 90)の後に、小文字の a(10 進数で 97)はすぐに続きません。その代わりに、6 つの文字 [, \\, ], ^, _, ` が続きます。カジュアルな観察者にとって、これはアルファベットを妨げるランダムな記号の並びに見えるでしょう。しかし、この隙間は偶然ではなく、コンピュータがテキストを扱う際の処理を簡素化するための意図的な設計選択です。
2 の力
コンピュータは二進データで動作し、ASCII(American Standard Code for Information Interchange)の設計者は当時のハードウェアに合わせてエンコーディングを最適化しました。Z と a の間の隙間を理解する鍵は、数値 32—2 のべき乗($2^5$)にあります。
英語のアルファベットは 26 文字です。大文字と小文字のセットの間に 6 つの追加文字を入れることで、設計者は任意の大文字とその小文字対応文字の間に正確に 32 コードポイントの距離を作り出しました。
バイナリ表現の比較
| 10進数 | 2進数 | 記号 |
|---|---|---|
| 65 | 01000001 |
A |
| 97 | 01100001 |
a |
| 66 | 01000010 |
B |
| 98 | 01100010 |
b |
| 67 | 01000011 |
C |
| 99 | 01100011 |
c |
すべての場合において、大文字と小文字の文字の唯一の違いは 5番目のビット($2^5$、すなわち 32 を表すビット)です。5番目のビットが 0 のとき文字は大文字、1 のとき小文字になります。
大文字小文字変換のビット演算マジック
ケースの違いが単一ビットに限定されているため、開発者は複雑な条件分岐やルックアップテーブルを使う代わりに、非常に高速なビット演算でケース変換を行うことができます。
大文字への変換
文字を大文字に強制するには、32 のビット単位の NOT とビット単位の AND を使用します。これにより、5番目のビットをクリアし、他のビットはそのまま残すマスクが作成されます。
ステップ 1: マスクを作成
~ 00100000 (32) $\rightarrow$ 11011111
ステップ 2: マスクを 'a' に適用
01100001 ('a') & 11011111 $\rightarrow$ 01000001 ('A')
小文字への変換
小文字への変換はさらに簡単です。32 とビット単位の OR を使用すれば、5番目のビットが 1 になることが保証されます(すでに 1 であっても同じです)。
01000001 ('A') | 00100000 (32) $\rightarrow$ 01100001 ('a')
大文字小文字の切り替え
文字のケースを反転させる必要がある場合(大文字から小文字、またはその逆)、32 とビット単位の XOR(排他的 OR)を使用すると 5番目のビットがトグルされます。
01100001 ('a') ^ 00100000 (32) $\rightarrow$ 01000001 ('A')
01000001 ('A') ^ 00100000 (32) $\rightarrow$ 01100001 ('a')
アルファベット順位置の決定
ケース変換以外にも、この構造は文字のアルファベット順位置(A が 1、B が 2 など)を素早く求める方法を提供します。31(2 進数で 00011111)とのビット単位の AND を行うことで、上位ビットをクリアし下位 5 ビットだけを残します。
これは数学的には character % 32 と等価です。なぜなら 32 は 2 のべき乗だからです。
'A'(65) $\rightarrow$ $65 \pmod{32} = 1$'Z'(90) $\rightarrow$ $90 \pmod{32} = 26$'a'(97) $\rightarrow$ $97 \pmod{32} = 1$'z'(122) $\rightarrow$ $122 \pmod{32} = 26$
この設計は、CPU サイクルが貴重だった時代を反映しています。アルファベットを二進境界に合わせることで、ASCII の設計者は一般的なテキスト操作タスクを可能な限り高効率で実行できるようにしました。