FlowingDev

10進数だけじゃない:コンピュータが16進数・2進数・8進数で数を数える仕組み

コンピュータが2進数(基数2)、16進数(基数16)、8進数といった基数を使って、色からネットワークアドレスまであらゆるものをどう表現しているのかを学びましょう。

ツールを試す: Base Calculator

一文で言うと

基数変換ツールは、私たちが日常で使うおなじみの10進数(基数10)と、すべてのデジタルロジックを動かす2進数(基数2)のような、異なる数の数え方の間で数値を翻訳してくれるものです。

これが解決する問題

人間は10という数字に取り憑かれています。指も足の指も10本ずつあるので、10をひとまとめにして数える10進数(基数10)は、息をするのと同じくらい自然に感じられます。でもコンピュータはどうでしょう? 彼らはもう少し単純です。その核心部分では、「オン」と「オフ」の2つしか理解しません。電流が流れているか、いないか。トランジスタがオンになっているか、オフになっているか。これが2進数、つまり基数2の世界です。このテキスト、お気に入りの猫の動画、OSそのものまで、コンピュータ内のあらゆる情報は、ただただ想像を絶するほど長い1と0の羅列にすぎません。

これが文化の衝突を生み出します。コンピューティングの初期、プログラマはプログラムをデバッグするために、この巨大な2進コードの壁を睨みつけなければなりませんでした。10110010...みたいな100万文字の文字列からタイプミスを見つけようとするところを想像してみてください。気が狂いそうになるし、間違いも起きやすいし、プログラマが壁に頭を打ち付ける主な原因でした。

正気(とおでこ)を保つために、彼らはショートハンド(速記法)を開発しました。ビットをグループ化できることに気づいたのです。3つずつグループ化すると8進数(基数8)になり、4つずつグループ化すると16進数(基死16)になります。突然、1011001011111110のような長い2進数の羅列が、ずっとコンパクトな16進数B2FEとして書けるようになったのです。

基数変換ツールは、この根本的な翻訳問題を解決するために存在します。人間の思考の10進数の世界と、我らがシリコンの下僕たちが住む2進数、8進数、16進数の世界との間の、万能な外交官なのです。

内部の仕組み

基数を変換する背後にある魔法は、実は魔法でもなんでもなく、単に位取り記数法と呼ばれる数の数え方のシステムなのです。

そもそも「基数」って何?

どんな基数でも、桁の位置がその値を決定します。10進数では当たり前のことですね。345という数字は、直感的に「3つの百と4つの十と5つの一」を意味するとわかります。

数学的には、こうなります: (3 * 10^2) + (4 * 10^1) + (5 * 10^0) = (3 * 100) + (4 * 10) + (5 * 1) = 300 + 40 + 5 = 345

「基数」とは、各桁でべき乗される数(この場合は10)のことです。同じルールがどんな基数にも適用されます。

2進数(基数2)では、0と1の2つの数字しか使いません。2進数の1011は次のようになります: (1 * 2^3) + (0 * 2^2) + (1 * 2^1) + (1 * 2^0) = (1 * 8) + (0 * 4) + (1 * 2) + (1 * 1) = 8 + 0 + 2 + 1 = 11 (10進数で)

16進数(基数16)は、16個のユニークな「数字」が必要なので、ちょいとクセがあります。最初の10個には0-9を使い、その後の10-15の値にはA-Fの文字を借ります。

10進数 16進数 2進数
0 0 0000
1 1 0001
... ... ...
9 9 1001
10 A 1010
11 B 1011
12 C 1100
13 D 1101
14 E 1110
15 F 1111

10進数から他の基数への変換

逆の方向、つまりおなじみの10進数から他の基数へ変換するには、繰り返し割り算を使います。187(10進数)を16進数(基数16)に変換してみましょう。

  1. 187を16で割る: 187 / 16 = 11 あまり 11。16進数では11はBです。
  2. 結果(11)を16で割る: 11 / 16 = 0 あまり 11。16進数では11はBです。
  3. あまりを下から上に読む: BB。

したがって、10進数の187は16進数でBBとなります。

特別な関係:2進数、8進数、16進数

ここに、初期のプログラマたちが発見した美しい近道があります。8 = 2^3 であり 16 = 2^4 なので、2進数から8進数や16進数へは、全く10進数に触れることなく変換できます。ビットをグループ化するだけです!

2進数 110101110100 を例に取ります。

  • 16進数(基数16)への変換: 右から4ビットずつグループ化します。 1101 0111 0100 次に、各グループを変換します: 1101 = D (13) 0111 = 7 0100 = 4 結果:16進数でD74。

  • 8進数(基数8)への変換: 右から3ビットずつグループ化します。 110 101 110 100 次に、各グループを変換します: 110 = 6 101 = 5 110 = 6 100 = 4 結果:8進数で6564。

これが、16進数と8進数がこれほど人気な理由です。これらは生のバイナリデータをコンパクトで人間が読みやすい形で表現できるのです。

ビット演算

プログラマ向けの計算機は、単なる基数変換以上のことができます。数値の2進数表現における個々の1と0を操作するビット演算も実行します。

  • AND (&): 両方のオペランドの対応するビットが両方とも1の場合に、各ビット位置で1を返します。特定のビットがオンになっているかを「マスキング」したりチェックしたりするのに便利です。 1101 & 1011 = 1001
  • OR (|): どちらか一方または両方のオペランドの対応するビットが1の場合に、各ビット位置で1を返します。ビットをオンに「設定」するのに役立ちます。 1101 | 1011 = 1111
  • XOR (^): 対応するビットが異なる場合に、各ビット位置で1を返します。ビットを反転(トグル)させるのに便利な方法です。 1101 ^ 1011 = 0110
  • NOT (~): すべてのビットを反転させます。 ~1101 = 0010 (4ビット数を想定)
  • ビットシフト (<<, >>): すべてのビットを左または右に押しやります。左シフト (<< 1) は2倍にする高速な方法で、右シフト (>> 1) は2で割る高速な方法です。

実社会での事例

謎のファイルタイプの事件簿

ある開発者が data.bin という名前の添付ファイル付きのメールを受け取りました。画像のはずなのに、コンピュータは開き方を知りません。彼女は諦めずに、ファイルをHexエディタで開きます。最初の数バイトは 89 50 4E 47 0D 0A 1A 0A と表示されました。彼女は以前にもこのような意味不明な文字列を見たことがあります。最初の4バイト 89504E47 を検索エンジンに放り込みます。結果はすぐに出ました。これはPNG画像の「マジックナンバー」、つまりファイル署名でした。彼女はファイル名を data.png に変更すると、見事、帽子をかぶった柴犬の素晴らしい画像が開きました。

教訓: 16進数は生データの言語です。これを理解することで、ファイルを最も基本的なレベルで特定し、デバッグすることができます。

CSSカラーの難問

あるフロントエンド開発者が髪をかきむしっていました。彼女はCSSで color: #FF0000; という美しく鮮やかな赤を指定しました。しかし、画面上では色が少し鈍い、赤とは言えない色になっています。彼女はブラウザの開発者ツールで要素を調査し、計算された色(computed color)が実際には #FE0000 であることを見つけます。基数変換ツールを使い、FFが10進数で255(カラーチャンネルの最大値)であるのに対し、FEが254であることを確認します。その差はわずかですが、確かに存在します。彼女は色の値をJavaScriptコードまで遡り、Math.round() の代わりに Math.floor() を使って浮動小数点数を整数に変換している関数を見つけました。254.99のような値が255 (FF) にならず、254 (FE) になっていたのです。すぐに修正すると、彼女の赤は完璧に鮮やかになりました。

教訓: 16進数はウェブカラーの共通言語(lingua franca)です。16進数の値を素早く変換し、それについて推論できることは、ピクセルパーフェクトなデザインとデバッグに不可欠です。

Linuxサーバーでのパーミッション設定

ある若手のシステム管理者が、サーバー上の設定ファイルを保護する必要がありました。先輩の管理者は彼に「パーミッションを rwx r-x --- に設定して」と言います。若手管理者はこれが所有者、グループ、その他の権限に対応することは知っていましたが、コマンドを忘れていました。彼はLinuxのパーミッションが8進数で表現できることを思い出します。彼はそれを分解しました:

  • 所有者: rwx (読み取り, 書き込み, 実行) = 4 + 2 + 1 = 7
  • グループ: r-x (読み取り, 実行) = 4 + 0 + 1 = 5
  • その他: --- (権限なし) = 0 + 0 + 0 = 0

彼はこれらを組み合わせて8進数の 750 を得ます。彼は自信を持って chmod 750 config.yml と入力し、先輩から頷きを得ました。

教訓: 8進数は、Unixのファイルパーミッションのような低レベルシステムで一般的なビットマスクを、簡潔かつ標準的な方法で表現する手段を提供します。

よくある間違いと落とし穴

  • プレフィックスのつけ忘れ: 10 という数字は、10進数、2進数、16進数で全く意味が異なります。コードやドキュメントでは、曖昧さを避けるために 0x(16進数、0x10 = 16)、0b(2進数、0b10 = 2)、0o(8進数、0o10 = 8)のようなプレフィックスを使います。常に文脈を意識してください。
  • 似た文字の混同: 疲れてメモリダンプを睨んでいると、Bと8、Dと0を混同しやすくなります。16進数の転記はダブルチェックしましょう。
  • 符号の有無(2の補数)の無視: 16進数で0xFFFFと表現される16ビット数は、符号なし整数であれば65535を意味するかもしれません。しかし、一般的な「2の補数」システムを使った符号付き整数であれば、それは-1を表します。ビットの意味は、それらがどう解釈されるかに完全に依存します。
  • エンディアンの悩み: 0x1A2B3C4Dのような複数バイトの数値がメモリに格納される際、バイトオーダーが異なることがあります。「ビッグエンディアン」システムは1A 2B 3C 4Dとして格納します。「リトルエンディアン」システム(ほとんどの現代のPCなど)は4D 3C 2B 1Aとして格納します。これは、エンディアンの異なる2つのシステムが通信しようとするときの、ネットワーキングやファイル解析における古典的なバグの原因です。

なぜ注目すべきか

高レベルな抽象化の裏側を覗き込むときはいつでも、異なる基数で考えるべきです。これには以下のような状況が含まれます:

  • ネットワークパケットの分析(例:MACアドレス、IPv6アドレス)。
  • ファイルフォーマットや生メモリダンプの操作。
  • ほとんど常に16進数で表示されるハッシュ値(例:SHA-256)の取り扱い。
  • ウェブカラーやビットマップグラフィックスの微調整。
  • ビット演算を使ったパフォーマンス重視のコード記述。
  • パーミッションやハードウェアレジスタのような低レベルシステム設定の構成。

基数を理解することは、開発者のスーパーパワーです。あなたが書くコードと、それに命を吹き込む電気パルスの間のギャップを埋めてくれます。

さらに深く

  • Positional notation on Wikipedia — すべての基数システムの数学的基礎である位取り記数法について。
  • Hexadecimal on Wikipedia — 基数16、その歴史、および一般的な用途についての詳細な解説。
  • Two's Complement on Wikipedia — コンピュータが2進数で負の数をどのように表現するかの決定版的な説明。
  • JavaScript Bitwise Operators on MDN — 人気のある言語でビット演算を使用するための実践的なガイド。
  • List of file signatures on Wikipedia — しばしば16進数で表現される「マジックナンバー」によってファイルを識別するための便利なリファレンス。
  • Endianness on Wikipedia — バイトの順序がなぜ思った以上に重要なのかを学ぶ。

理論はOK。さあ手を動かそう — 100%ブラウザ内で。

ツールを試す: Base Calculator