基数変換ガイド:2進数・16進数・8進数・10進数の相互変換と実践
ある午後、デバッガで 0x7FFF5FBFF8C0 を眺め、CSSファイルで #FF5733 を調整し、ターミナルで chmod 755 を叩く。3つの異なる数値表現だが、根底にあるのは同じ算術だ。16進数と2進数の変換で手が止まったり、Unixのパーミッションがなぜ8進数なのか疑問に思ったりしたことがあるなら、この記事でそれらの繋がりが明確になるはずだ。
プログラミングで日常的に使う4つの基数、覚えておくべき3つの変換方法、そしてJavaScript・Python・Go・Cの実コードを順に解説する。理論を飛ばして数値を変換したいだけなら、基数変換ツールを開こう。2から36までの任意の基数に対応し、精度に制限はない。
開発者が使う4つの基数
各基数がプログラミングに存在するのは実用的な理由による。
2進数(Base 2)——マシンの言語
使う数字は 0 と 1 の2つ。トランジスタはオンかオフか——この物理的制約が2進数を生んだ。ビットマスク、フラグ管理、ビット演算、IPサブネット計算で直接扱うことになる。
2進数はすぐに長くなる。10進数の255は2進数で 11111111——3桁の値に8桁が必要だ。個々のビット位置に意味がある場面以外、生の2進数を書く場面は少ない。
8進数(Base 8)——Unixの略記法
使う数字は 0 から 7 の8つ。各8進数字はちょうど3ビットに対応する。Unixファイルパーミッションが8進数を使うのはそのためだ。chmod 755 は3つの3ビットグループを3桁の読みやすい数字に圧縮している。
PDP-11の時代にはもっと広く使われていたが、今はほぼUnixパーミッション専用だ。Cの 0 プレフィックスリテラル(0177 を10進数の177だと思って書くバグの原因)で見かけることもある。
10進数(Base 10)——人間のデフォルト
使う数字は 0 から 9 の10個。脳が自然に使う基数であり、ポート番号、配列インデックス、HTTPステータスコード、ピクセル寸法はすべて10進数だ。コンピュータ内部は10進数で動いていないが、人間がそうなので、ユーザー向けの数値はすべてBase 10で出力される。
16進数(Base 16)——開発者のスイスアーミーナイフ
16個のシンボル:0-9 と A-F。各16進数字はちょうど4ビット(1ニブル)を表す。16進2桁 = 1バイト。常にそうだ。
メモリアドレス(0x7FFF5FBFF8C0)、CSSカラー(#FF5733)、MACアドレス(00:1A:2B:3C:4D:5E)、UUIDフォーマット、ハッシュダイジェストで16進数に出会う。バイトレベルプログラミングの共通言語だ。
基数変換ツールで4つの基数間の変換を試そう。任意の基数で値を入力すれば、残りがリアルタイムで更新される。
基数変換の仕組み:3つの基本手法
ツールなしでも変換はできる。3つの手法ですべてのケースをカバーする。
手法1——位取り展開(任意の基数 → 10進数)
位取り記数法はすべて同じ仕組みだ。各桁の数字に「基数のその桁乗」を掛けて合計する。右端が0乗。
2進数 1011:
1×2³ + 0×2² + 1×2¹ + 1×2⁰
= 8 + 0 + 2 + 1
= 11
16進数 FF:
15×16¹ + 15×16⁰
= 240 + 15
= 255
コードでは、ほとんどの言語に解析関数がある:
parseInt('1011', 2) // 11
parseInt('FF', 16) // 255
parseInt('755', 8) // 493
手法2——連続除算(10進数 → 任意の基数)
逆方向の変換は、10進数を目標基数で繰り返し割り、余りを集めて下から上に読む。
10進数 255 → 16進数:
255 ÷ 16 = 15 余り 15 (F)
15 ÷ 16 = 0 余り 15 (F)
→ 下から読む:FF
10進数 42 → 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
bin(42) # '0b101010'
hex(255) # '0xff'
oct(493) # '0o755'
手法3——ビットグルーピング(2進数 ↔ 16進数/8進数の直接変換)
経験豊富な開発者が最も頻繁に使う方法だ。16 = 2⁴、8 = 2³ なので、2進数と16進数(または8進数)の間はビットをグループ化するだけで変換できる。計算不要。
2進数 → 16進数: 右から4ビットずつ(ニブル)にグループ化。左端が足りなければゼロで埋める。
2進数: 1010 1111
16進数: A F
→ AF
2進数 → 8進数: 右から3ビットずつにグループ化。
2進数:111 101 101
8進数: 7 5 5
→ 755
ニブル対応表は暗記する価値がある:
| 2進数 | 16進数 | 2進数 | 16進数 |
|---|---|---|---|
0000 | 0 | 1000 | 8 |
0001 | 1 | 1001 | 9 |
0010 | 2 | 1010 | A |
0011 | 3 | 1011 | B |
0100 | 4 | 1100 | C |
0101 | 5 | 1101 | D |
0110 | 6 | 1110 | E |
0111 | 7 | 1111 | F |
この表を覚えれば、2進数から16進数への変換は一瞬だ。
各言語での基数変換コード
基数変換が頻繁に登場する4言語の実用コード。
JavaScript / TypeScript
// 解析:任意基数の文字列 → 数値
parseInt('FF', 16) // 255
parseInt('101010', 2) // 42
parseInt('755', 8) // 493
// フォーマット:数値 → 任意基数の文字列
(255).toString(16) // 'ff'
(42).toString(2) // '101010'
(493).toString(8) // '755'
// リテラル
const bin = 0b11111111; // 255
const oct = 0o377; // 255
const hex = 0xff; // 255
// BigInt:2⁵³を超える値に対応
const big = BigInt('0xFFFFFFFFFFFFFFFF');
big.toString(2) // 64個の1
big.toString(10) // '18446744073709551615'
Python
# 10進数 → 他の基数(プレフィックス付き文字列)
bin(255) # '0b11111111'
oct(493) # '0o755'
hex(255) # '0xff'
# 他の基数 → 10進数
int('11111111', 2) # 255
int('FF', 16) # 255
int('755', 8) # 493
# パディング付きフォーマット出力
f'{255:08b}' # '11111111'(8桁2進数、ゼロ埋め)
f'{255:02x}' # 'ff'(16進数、小文字)
f'{255:02X}' # 'FF'(16進数、大文字)
# Pythonの整数は標準で任意精度
big = int('F' * 64, 16) # 256ビットの数値、オーバーフローなし
Go
package main
import (
"fmt"
"strconv"
)
func main() {
// フォーマット:int → 任意基数の文字列
fmt.Println(strconv.FormatInt(255, 16)) // "ff"
fmt.Println(strconv.FormatInt(255, 2)) // "11111111"
fmt.Println(strconv.FormatInt(493, 8)) // "755"
// 解析:任意基数の文字列 → int
n, _ := strconv.ParseInt("FF", 16, 64) // 255
fmt.Println(n)
// Printf動詞
fmt.Printf("%b %o %x %d\n", 255, 255, 255, 255)
// 11111111 377 ff 255
}
C
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
int main() {
// 10進数、8進数、16進数で出力
printf("%d %o %x\n", 255, 255, 255);
// 255 377 ff
// 任意基数から解析
long val = strtol("FF", NULL, 16); // 255
long bin = strtol("101010", NULL, 2); // 42
// Cには2進数出力のprintf書式がない——手動でビット抽出:
uint8_t byte = 0xAF;
for (int i = 7; i >= 0; i--)
putchar(((byte >> i) & 1) ? '1' : '0');
// 10101111
return 0;
}
実務での基数変換シナリオ
基数変換が教科書の知識ではなく実務になる5つの場面。
1. メモリアドレスのデバッグ
デバッガがポインタを 0x7FFF5FBFF8C0 と表示している。末尾2バイトを2進数に変換すると 1000 1100 0000 ——末尾に6個のゼロがあり、64バイト境界にアラインされていることがわかる。アラインメントはキャッシュ性能、SIMD演算、メモリマップドI/Oに影響する。
ポインタ演算も16進数の方が把握しやすい。ベースアドレスから 0x100 オフセットは正確に256バイト——多くの組み込みシステムで1ページ分だ。
2. CSS 16進カラー ↔ RGB
#FF5733 は3バイトの16進ペア:
| ペア | 16進数 | 10進数 | チャネル |
|---|---|---|---|
FF | FF | 255 | 赤(最大値) |
57 | 57 | 87 | 緑 |
33 | 33 | 51 | 青 |
省略記法の #F00 は #FF0000(純粋な赤)に展開される。8桁表記の #FF573380 はアルファチャネルを追加し、80(10進数128)は約50%の不透明度を意味する。
3. Unixファイルパーミッション(8進数)
chmod 755 の内訳:
7 → 111 → rwx(所有者:読み取り+書き込み+実行)
5 → 101 → r-x(グループ:読み取り+実行)
5 → 101 → r-x(その他:読み取り+実行)
各8進数字がパーミッショングループ1つをエンコードする。3つのパーミッションビット(読み取り=4、書き込み=2、実行=1)が1つの8進数字(0-7)に対応するからだ。よく使う組み合わせ:
| 8進数 | 2進数 | パーミッション | 典型的な用途 |
|---|---|---|---|
755 | 111 101 101 | rwxr-xr-x | 実行ファイル、ディレクトリ |
644 | 110 100 100 | rw-r--r-- | 通常のファイル |
700 | 111 000 000 | rwx------ | プライベートスクリプト |
600 | 110 000 000 | rw------- | SSH鍵、秘密情報 |
4. ネットワークサブネット計算
/24 サブネットマスクは2進数で先頭24個が1:
11111111.11111111.11111111.00000000
→ 255.255.255.0
ネットワークアドレスを求めるには、IPとマスクをビットANDする:
192.168.1.37 → 11000000.10101000.00000001.00100101
255.255.255.0 → 11111111.11111111.11111111.00000000
AND結果 → 11000000.10101000.00000001.00000000
→ 192.168.1.0(ネットワークアドレス)
ネットワークエンジニアは10進数(IP表記)、2進数(サブネット計算)、16進数(パケットキャプチャ)を日常的に行き来する。基数変換ツールで各オクテットを個別に検証できる。
5. ハッシュダイジェストとUUIDの読み方
MD5ハッシュ d41d8cd98f00b204e9800998ecf8427e は16進数32文字 = 16バイト(128ビット)。16進2桁が1バイトに対応する。
UUIDは 8-4-4-4-12 の16進数フォーマット:
550e8400-e29b-41d4-a716-446655440000
ハイフン区切りの16進32桁——同じ128ビットの別のフォーマットだ。13番目のニブル(41d4 の 4)がUUID v4を示す。
MD5 & SHAハッシュジェネレーターでハッシュを生成、UUIDジェネレーターでUUIDを作成できる。どちらも16進数出力だ。
Base 16を超えて:Base 36、Base 64、カスタム基数
標準の4つの基数でほとんどの仕事はカバーできるが、特殊な文脈で他の基数も登場する。
Base 36——コンパクトな英数字エンコーディング
Base 36は数字10個と英字26文字(A-Z)を使い、大文字小文字を区別しない英数字表現として最もコンパクトだ。URL短縮サービスでよく使われる——YouTubeの動画ID、短縮リンク、データベースの短いキーなどだ。
(1000000).toString(36) // 'lfls'
parseInt('lfls', 36) // 1000000
10進数1,000,000がたった4文字に。URL向けの短い識別子としてこれ以上コンパクトにするのは難しい。
Base 64——データエンコーディング(数値体系ではない)
Base64は別の基数のように見えるが、目的が異なる。数値を位取り記数法で表すのではなく、任意のバイナリデータ(画像、ファイル、JWTトークン)をASCIIテキストにエンコードする仕組みだ。A-Z、a-z、0-9、+、/ の64シンボルを使う。
Base64は3バイト(24ビット)を4文字の6ビット単位に変換する。エンコーディングスキームであり、数値体系ではない。Base64のエンコード・デコードにはBase64エンコーダー&デコーダーを使おう。
任意の基数(2-36)が登場する場面
他にもいくつかの基数が現れる:
- Base 12(十二進法): 時間(12時間制)、数量(1ダース)。12は10より約数が多いため、日常使用にはBase 12の方が優れているという数学者もいる。
- Base 60(六十進法): 時間(60秒、60分)と角度(360°)。バビロニア数学から受け継がれた。
- Base 32: Crockford Base32エンコーディング。人間が読みやすい識別子用で、I・L・Oなど紛らわしい文字を除外する。ジオハッシュにも使われる。
基数変換ツールは2から36までの任意の整数基数をサポートしている。
ビット演算と基数変換
2進数の理解は数値変換だけでなく、システムプログラミング、ゲーム開発、パーミッションシステムで頻出するビット演算の土台でもある。
const READ = 0b100; // 4
const WRITE = 0b010; // 2
const EXEC = 0b001; // 1
// ORでパーミッションを結合
const perms = READ | WRITE; // 0b110 = 6
// ANDでパーミッションを確認
(perms & READ) !== 0 // true — 読み取り権限あり
(perms & EXEC) !== 0 // false — 実行権限なし
// XORでパーミッションを切り替え
perms ^ WRITE // 0b100 = 4 — 書き込み権限を除去
// シフト演算
1 << 3 // 0b1000 = 8(1を左に3ビットシフト)
0xFF >> 4 // 0b00001111 = 15(右に4ビットシフト = 16で割る)
フィーチャーフラグ、ハードウェアレジスタ、ネットワークプロトコル、グラフィックスプログラミング——すべてビット演算に依存している。2進数を流暢に読めるようになれば、ビット操作は魔法ではなく明快なロジックに変わる。
FAQ
プログラミングで使われる主な4つの基数は?
2進数(Base 2)、8進数(Base 8)、10進数(Base 10)、16進数(Base 16)。2進数はハードウェア内のデータの物理的な存在形式。8進数はUnixファイルパーミッション用。10進数は人間向けのデフォルト。16進数は2進数を読みやすく圧縮する——16進1桁がちょうど4ビットに対応する。
2進数を16進数に変換するには?
右から4ビットずつグループ化し、左端が不足すればゼロで埋める。各グループを対応表で変換:0000=0、0001=1、……1010=A、……1111=F。例:2進数 10101111 → グループ 1010 1111 → 16進数 AF。16 = 2⁴ だから成り立つ。
16進数を10進数に変換するには?
各16進桁に16のその桁乗を掛けて合計する。A=10、B=11、C=12、D=13、E=14、F=15。例:16進数 FF = 15×16¹ + 15×16⁰ = 240 + 15 = 255。コード:JavaScript parseInt('FF', 16)、Python int('FF', 16)。
開発者が2進数ではなく16進数を使う理由は?
16進数はコンパクトだ。16進1桁がちょうど4ビットに対応するため、2進数の 11111111 00001010 は16進数で FF0A になる。短くて読みやすい。メモリアドレス、CSSカラー(#FF5733)、MACアドレス、ハッシュ出力、UUIDフォーマットの標準表記として使われている。
ニブルとは何か。16進変換との関係は?
ニブルは4ビットのグループ、つまりバイトの半分。1ニブルがちょうど1つの16進桁に対応するため、2進数から16進数への変換はニブルごとの対照表参照で完了する。1バイト = 2ニブル = 16進2桁。この整った4ビット対応が、16進数がバイトレベルデータの標準表現になった理由だ。
Unixファイルパーミッションが8進数表記なのはなぜ?
各パーミッショングループには3ビットある:読み取り(4)、書き込み(2)、実行(1)。グループは3つ(所有者、グループ、その他)。2³ = 8 なので、3つのパーミッションビットが1つの8進桁にちょうど対応する。755 は所有者=7(rwx)、グループ=5(r-x)、その他=5(r-x)。8進数は3ビットグルーピングにとって自然な基数だ。
JavaScriptで2⁵³を超える数値を扱うには?
BigInt を使う。リテラルに n を付けるか、BigInt() コンストラクタを使用する:BigInt('0xFFFFFFFFFFFFFFFF').toString(2) で完全な64ビット2進数文字列が得られる。標準の Number は 9,007,199,254,740,991(2⁵³ - 1)を超えると精度が落ちる。基数変換ツールは内部でBigIntを使用しており、どんなサイズの数値でも精度を失わない。
16進数を2進数に変換するには?
ニブル対応表を逆引きして、各16進桁を4ビット2進数に展開する。0=0000、1=0001、……、A=1010、……、F=1111。例:16進数 AF → A(1010)+ F(1111)→ 2進数 10101111。コード:JavaScript parseInt('AF', 16).toString(2)、Python bin(int('AF', 16))[2:]。
Pythonの bin() とNumPyの numpy.base_repr() はどう違うか?
組み込みの bin()・hex()・oct() は整数専用で、プレフィックス付き文字列を返す(bin(10) → '0b1010')。NumPyの numpy.base_repr(n, base) はプレフィックスなしの文字列を返し、任意の基数(2〜36)に対応する(np.base_repr(10, 2) → '1010')。科学計算や配列全体を一括変換する場合は numpy.base_repr をベクトル化ラッパー経由で使うと高速だが、単発の変換には組み込み関数のほうが依存もなく簡潔。