2進数・8進数・16進数の表記方法をわかりやすく解説

コンピュータの世界では、10進数以外の基数が頻繁に使われます。2進数、8進数、16進数はそれぞれの用途に応じて使い分けられています。

数値の頭にプレフィックスを付けることで、どの基数で表現されているかを明示します。

本記事の内容

  • 各基数の表記法とプレフィックス
  • 基数変換の数学的原理
  • ビット演算との関係
  • Pythonでの基数変換の実装

基数と位取り表記

$b$ 進数では、$n$ 桁の数値 $N$ は次のように表されます。

$$ N = \sum_{k=0}^{n-1} d_k \cdot b^k = d_{n-1} b^{n-1} + d_{n-2} b^{n-2} + \cdots + d_1 b + d_0 $$

ここで $d_k$ は各桁の数字で、$0 \leq d_k < b$ です。

プレフィックスの対応

基数 英語名 プレフィックス 使用する数字
2 binary 0b 0, 1
8 octal 0o 0-7
10 decimal なし 0-9
16 hexadecimal 0x 0-9, A-F

例えば、10進数の42は各基数で次のように表されます。

$$ 42_{10} = \text{0b101010}_2 = \text{0o52}_8 = \text{0x2A}_{16} $$

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

10進数 $N$ を $b$ 進数に変換するには、$N$ を $b$ で繰り返し割り、余りを逆順に並べます。

$$ N = q_0 \cdot b + r_0, \quad q_0 = q_1 \cdot b + r_1, \quad \ldots $$

商が0になるまで繰り返し、余りの列 $r_0, r_1, \ldots$ を逆順に並べたものが $b$ 進数表現です。

具体例: 42を2進数に変換

$$ \begin{align} 42 &= 21 \times 2 + 0 \\ 21 &= 10 \times 2 + 1 \\ 10 &= 5 \times 2 + 0 \\ 5 &= 2 \times 2 + 1 \\ 2 &= 1 \times 2 + 0 \\ 1 &= 0 \times 2 + 1 \end{align} $$

余りを逆順に読むと $101010_2$ となります。

16進数とバイト表現

16進数はバイト(8ビット)の表現に特に便利です。1バイトはちょうど16進数2桁で表現できます。

$$ 1 \text{ byte} = 8 \text{ bits} = 2 \text{ hex digits} $$

例えば、メモリアドレス 0x7FFF0000 は32ビット(4バイト)のアドレスを表しています。

ビット演算

2進数表現はビット演算と密接に関係しています。

AND演算

$$ a \text{ AND } b = a \wedge b $$

特定のビットを抽出する(マスク操作)ために使用されます。

OR演算

$$ a \text{ OR } b = a \vee b $$

特定のビットをセットするために使用されます。

シフト演算

左シフト $n$ ビットは $2^n$ 倍、右シフト $n$ ビットは $2^n$ で割ることに相当します。

$$ x \ll n = x \times 2^n, \quad x \gg n = \lfloor x / 2^n \rfloor $$

Pythonでの基数変換と可視化

import numpy as np
import matplotlib.pyplot as plt

def decimal_to_base(n, base):
    """10進数をb進数に変換"""
    if n == 0:
        return "0"
    digits = []
    hex_chars = "0123456789ABCDEF"
    is_negative = n < 0
    n = abs(n)

    while n > 0:
        digits.append(hex_chars[n % base])
        n //= base

    if is_negative:
        digits.append('-')

    return ''.join(reversed(digits))

def base_to_decimal(s, base):
    """b進数を10進数に変換"""
    hex_vals = {c: i for i, c in enumerate("0123456789ABCDEF")}
    result = 0
    for c in s.upper():
        result = result * base + hex_vals[c]
    return result

# 変換の例
print("=== 基数変換の例 ===")
for n in [0, 1, 10, 42, 127, 255, 1024, 65535]:
    bin_str = decimal_to_base(n, 2)
    oct_str = decimal_to_base(n, 8)
    hex_str = decimal_to_base(n, 16)
    print(f"{n:6d} = 0b{bin_str:>16s} = 0o{oct_str:>6s} = 0x{hex_str:>4s}")

# Python組み込み関数との比較
print("\n=== Python組み込み関数 ===")
n = 255
print(f"bin({n}) = {bin(n)}")
print(f"oct({n}) = {oct(n)}")
print(f"hex({n}) = {hex(n)}")

# ビット演算の可視化
fig, axes = plt.subplots(2, 2, figsize=(12, 8))

# AND演算
a, b = 0b11001010, 0b10101100
result_and = a & b
axes[0, 0].set_title(f'AND: {bin(a)} & {bin(b)} = {bin(result_and)}')
bits_a = [(a >> i) & 1 for i in range(7, -1, -1)]
bits_b = [(b >> i) & 1 for i in range(7, -1, -1)]
bits_r = [(result_and >> i) & 1 for i in range(7, -1, -1)]
x = range(8)
axes[0, 0].bar([i-0.2 for i in x], bits_a, 0.2, label='A', color='blue', alpha=0.7)
axes[0, 0].bar(x, bits_b, 0.2, label='B', color='red', alpha=0.7)
axes[0, 0].bar([i+0.2 for i in x], bits_r, 0.2, label='A&B', color='green', alpha=0.7)
axes[0, 0].legend()
axes[0, 0].set_ylabel('Bit Value')

# OR演算
result_or = a | b
axes[0, 1].set_title(f'OR: {bin(a)} | {bin(b)} = {bin(result_or)}')
bits_r_or = [(result_or >> i) & 1 for i in range(7, -1, -1)]
axes[0, 1].bar([i-0.2 for i in x], bits_a, 0.2, label='A', color='blue', alpha=0.7)
axes[0, 1].bar(x, bits_b, 0.2, label='B', color='red', alpha=0.7)
axes[0, 1].bar([i+0.2 for i in x], bits_r_or, 0.2, label='A|B', color='green', alpha=0.7)
axes[0, 1].legend()
axes[0, 1].set_ylabel('Bit Value')

# 左シフト
val = 0b00001010
shifts = range(8)
shifted_vals = [val << s for s in shifts]
axes[1, 0].bar(shifts, shifted_vals, color='steelblue')
axes[1, 0].set_xlabel('Shift Amount')
axes[1, 0].set_ylabel('Value')
axes[1, 0].set_title(f'Left Shift: {val} << n')

# 基数ごとの桁数
values = np.arange(1, 1001)
digits_2 = np.floor(np.log2(values)) + 1
digits_8 = np.floor(np.log2(values) / np.log2(8)) + 1
digits_16 = np.floor(np.log2(values) / np.log2(16)) + 1
digits_10 = np.floor(np.log10(values)) + 1

axes[1, 1].plot(values, digits_2, label='Binary')
axes[1, 1].plot(values, digits_8, label='Octal')
axes[1, 1].plot(values, digits_10, label='Decimal')
axes[1, 1].plot(values, digits_16, label='Hex')
axes[1, 1].set_xlabel('Value')
axes[1, 1].set_ylabel('Number of Digits')
axes[1, 1].set_title('Number of Digits vs Value')
axes[1, 1].legend()
axes[1, 1].grid(True)

plt.tight_layout()
plt.show()

まとめ

本記事では、2進数・8進数・16進数の表記方法と基数変換について解説しました。

  • $b$ 進数は各桁の数字と $b$ の累乗の重み付き和で10進数に変換できる
  • 10進数からの変換は、繰り返し割り算で余りを逆順に並べる
  • 16進数は1バイト = 2桁の対応でメモリ表現に便利
  • ビット演算(AND, OR, シフト)は2進数表現と直結する
  • Pythonでは bin(), oct(), hex() で簡単に基数変換ができる