コンピュータの世界では、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()で簡単に基数変換ができる