2進数と16進数は、同じ数値を、異なる「桁の重み」で書く表記です。たとえば、10進数の65、2進数の01000001、16進数の41は、いずれも同じ数値を表せます。コンピュータが16進数で処理しているのではなく、コンピュータが扱うbit列を、人が4bitずつ短く読み書きするために16進数を使います。

0x41だけを見て「文字A」と決めることはできません。まず分かるのは「16進数で41と書かれた値」です。UTF-8の文字データとして読む、という文脈が加わるとAになります。
進数の本質は「各桁にいくつの重みを持たせるか」
普段使う10進数も、2進数や16進数と同じ位取り記数法です。右端の桁を0番目とすると、各桁には「基数の0乗、1乗、2乗……」という重みがあります。基数とは、その進数で1桁に使える数字の種類の数です。
| 表記 | 基数 | 右から並ぶ桁の重み | 65の書き方 |
|---|---|---|---|
| 10進数 | 10 | 1、10、100、1000… | 65 |
| 2進数 | 2 | 1、2、4、8、16、32、64… | 1000001 |
| 16進数 | 16 | 1、16、256、4096… | 41 |
たとえば10進数の365は、「3、6、5」という数字を並べただけではありません。各桁の数字へ100、10、1の重みを掛けて足した値です。
365(10進)
= 3 × 10^2 + 6 × 10^1 + 5 × 10^0
= 3 × 100 + 6 × 10 + 5 × 1
右端が1の重みになるのは、どの進数でも「基数の0乗が1」だからです。同じ規則で、2進数の1000001は1 × 2^6 + 1 × 2^0、16進数の41は4 × 16^1 + 1 × 16^0と読めます。
1000001(2進) = 1 × 64 + 1 × 1 = 65
41(16進) = 4 × 16 + 1 × 1 = 65
つまり、2進数だけに特別な計算原理があるわけではありません。何進数かによって、桁が1つ左へ移るたびに何倍になるかが変わるだけです。
先頭の0は値を変えないが、桁数の情報は消してはいけない
1000001と01000001は、符号なしの2進数として計算すれば、どちらも65です。先頭に0を足しても、0と桁の重みを掛けた分が増えるだけなので、値は変わりません。16進数の41と0041も同じです。
しかし、01000001と8桁で書けば「8bit幅で表示している」ことが分かりやすくなります。1byteを表示するなら8bit、16進数では2桁です。反対に、ログが先頭0を省略すると、数字だけから元のデータ幅を復元できないことがあります。
n bitで表せるのは2n通り
1bitには0と1の2通りがあります。2bitなら、1桁目の2通りそれぞれに2桁目の2通りを組み合わせるため、2 × 2 = 4通りです。同じ考えをn桁へ延ばすと、組み合わせは2^n通りになります。
| 幅 | bitパターンの数 | 符号なし整数としての範囲 |
|---|---|---|
| 1bit | 21 = 2通り | 0〜1 |
| 4bit | 24 = 16通り | 0〜15 |
| 8bit | 28 = 256通り | 0〜255 |
| n bit | 2n通り | 0〜2n−1 |
8bitの最大値は255ですが、0も1通りとして数えるため、パターンの総数は255通りではなく256通りです。bitとbyteそのものを先に整理したい場合は、ビットとバイトの違いを確認してください。
memo.txtのAが01000001になるまで
ここからは、新しいmemo.txtへ半角のAだけを入力し、UTF-8・BOMなし・改行なしで保存する例に絞ります。ファイルに別の文字、改行、BOMがあれば、その分のbyteも加わります。
- 文字を番号へ対応させる:Unicodeでは、文字Aにコードポイント
U+0041が割り当てられています。U+の後ろは16進数で書かれたコードポイントです。 - 番号をUTF-8のbyte列へ符号化する:UTF-8では、
U+0000〜U+007Fの範囲を同じ値の1byteで表します。そのためAは、16進表記で41の1byteになります。 - 1byteを8bitで書く:byte値
41を2進表記に直すと01000001です。

U+0041と0x41は、Aでは数値が一致しますが、役割は別です。前者はUnicodeコードポイントを指す表記、後者はここではUTF-8で出力された1byteの値を16進数で示す表記です。ASCII範囲を外れる文字では、コードポイントとUTF-8の各byte値はこのように単純には一致しません。

ファイルの中に、紙に書いたような「A」や文字列の「01000001」が入るわけではありません。保存されるのはbyte列です。エディタがそのbyte列をUTF-8として復号し、フォントで描画することで、画面にAが見えます。
文字、コードポイント、符号化されたbyte列の違いは、文字コードとはで詳しく扱っています。本記事では、得られた1byteを2進数と16進数でどう読み替えるかへ戻ります。
2進数から10進数へ:1の立っている桁の重みを足す
01000001を10進数へ変換するときは、数字列を丸ごと暗記する必要はありません。各桁の重みを並べ、bitが1の場所だけを足します。
bit位置 7 6 5 4 3 2 1 0
桁の重み 128 64 32 16 8 4 2 1
bit 0 1 0 0 0 0 0 1
- 右端から
2^0、2^1、2^2…の重みを割り当てる - bitが1の桁を探す
- その桁の重みを足す
01000001(2進)
= 0×128 + 1×64 + 0×32 + 0×16
+ 0×8 + 0×4 + 0×2 + 1×1
= 64 + 1
= 65(10進)
2進数から10進数への変換は、bit列から各桁の重みを使って値を再構成する作業です。1の位置さえ分かれば、足す対象も分かります。
10進数から2進数へ:値を2のべき乗へ分解する
反対に65を2進数へ直すときは、65を「2のべき乗の和」へ分解します。今回は1byte、つまり8bitで表す前提なので、最大の重み128から順に確認します。
| 調べる重み | 残りの値に含められるか | bit | 残り |
|---|---|---|---|
| 128 | 65より大きい | 0 | 65 |
| 64 | 含められる | 1 | 1 |
| 32、16、8、4、2 | 残り1より大きい | すべて0 | 1 |
| 1 | 含められる | 1 | 0 |
65 = 64 + 1 = 2^6 + 2^0なので、6番目と0番目のbitを1にし、それ以外を0にします。その結果が01000001です。
65 = 64 + 1
= 2^6 + 2^0
= 01000001(8bitの2進表記)
数値65だけを表すなら先頭0を省いた1000001でも同じ値です。今回は「UTF-8で出力された1byte」を見ているため、8桁へそろえて01000001と書いています。
2進数と16進数:4bitずつ置き換えればよい
16進数1桁には0〜15の16通りがあります。2進数4桁も2^4 = 16通りです。さらに16 = 2^4なので、2進数4桁と16進数1桁を、情報を失わず1対1で対応させられます。
| 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進数へ変換する
右端から4bitずつ区切り、それぞれを16進数1桁へ置き換えます。左端のグループが4bitに満たなければ、値を変えない先頭0で埋めます。
01000001
→ 0100 0001
→ 4 1
→ 41(16進)
16進数から2進数へ変換する
逆向きは、16進数の各桁を必ず4bitへ置き換えます。4 → 0100、1 → 0001なので、41 → 01000001です。
この対応により、1byteは2桁、32bitは8桁、64bitは16桁の16進数で表せます。デバッガや通信仕様で16進数が多いのは、長いbit列を短くしながら、4bitの境界を崩さず戻せるためです。
0xと0bは値ではなく、読み方を示す目印
41だけでは、10進数の41なのか、16進数の65なのかを判別できません。そこでPythonやJavaなどのソースコードでは、0xを16進整数リテラル、0bを2進整数リテラルの接頭辞として使います。
65 # 10進表記
0b01000001 # 2進表記
0x41 # 16進表記
# いずれも同じ整数値65を表す
0xや0bは、数字列を何進数として読むかをソースコードの字句解析器や人へ伝える記号です。値65の中に「0x」という成分があるわけでも、実行時のレジスタへ文字0とxが一緒に入るわけでもありません。
| 表記例 | 何を示すか | 注意点 |
|---|---|---|
0x41 |
16進数の数値表記 | PythonやJavaなどで使われる。製品によっては接頭辞を付けない |
0b01000001 |
2進数の数値表記 | 0bを受け付けるかは言語・書式の仕様次第 |
U+0041 |
Unicodeコードポイントの表記 | U+は16進数のコードポイントを示し、UTF-8のbyte列そのものではない |
41(hex欄) |
欄の定義によって16進数と分かる表記 | ログや16進ダンプでは接頭辞が省かれることがある |
表記法は共通の自然法則ではなく、言語や製品が決める構文です。たとえばPythonの言語リファレンスとJava言語仕様は、どちらも0xと0bを定めていますが、別のツールやデータ形式へそのまま貼り付けられるとは限りません。
同じbit列でも、意味は型・符号化・仕様が決める
bit列は、それだけでは「文字」「正の整数」「負の整数」「命令」といった札を持っていません。どの規則で読むかが決まって、初めて意味が生まれます。
| bit列 | 読む規則 | 得られる意味 |
|---|---|---|
01000001 |
8bitの符号なし整数 | 65 |
01000001 |
UTF-8の文字データ | 文字A |
01000001 |
16進表示 | 41 |
01000001 |
命令・画像・通信データ中の1byte | その仕様と位置が定めるフィールドやデータの一部 |
符号付きと符号なしの違いも、同じ原理です。8bitの11111111を符号なし整数として読めば255ですが、8bitの2の補数による符号付き整数として読めば−1です。bitが途中で変わったのではなく、型が適用する解釈規則が変わったのです。
文字なら文字コード、数値ならデータ型、通信データならプロトコル、機械語なら命令セットアーキテクチャ(ISA)が解釈を決めます。CPUも「このbit列はAだ」と自由に判断するのではなく、実行中の命令と定められた命令形式に従ってbitを処理します。CPU側の続きを知りたい場合は、CPUとはへ進んでください。
「コンピュータは16進数で処理する」は誤解
16進数は、bit列を人間向けに表示する方法です。ソースコードに0x41と書けば、コンパイラや言語処理系が「整数値65を表すリテラル」として解析します。実行時のCPUやメモリに、接頭辞0x付きの形がそのまま格納されるわけではありません。
デバッガ、メモリダンプ、文字コード表、通信ログが16進数を使うのは、1桁が4bitへ正確に対応し、2進数より短く読めるからです。10進数でも同じ値を表示できますが、bit境界との対応は見えにくくなります。
| よくある誤解 | 正しい捉え方 |
|---|---|
| コンピュータは2進数と16進数を使い分けて計算する | ハードウェア上の状態はbitとして扱われ、16進数は主に人が読む表記 |
01000001は必ずAである |
UTF-8など、Aへ対応させる符号化の文脈が必要 |
0xも値の一部である |
16進数として読むための表記上の目印 |
| 先頭0はいつでも無意味である | 整数値は変えないが、表示幅やフィールド幅を考える手掛かりになる |
| 8bitなら最大255なので255通りである | 0を含むため256通り。符号なしなら0〜255 |
実際のログやダンプは「表記・幅・解釈」の順で読む
たとえば、UTF-8・BOMなし・改行なしでAだけを保存したmemo.txtを、Unix系環境のodで確認すると、16進数のbyte値と文字表示を分けて見られます。
$ od -An -tx1 -tc memo.txt
41
A
1行目の41は、-tx1で「1byte単位の16進数」と指定した結果です。2行目のAは、-tcで文字として表示した結果です。同じbyteを別の読み方で見せており、ファイル内に「41」という2文字も追加保存されているわけではありません。
- 表記を確認する:
0x、0b、列見出し、仕様書から何進数かを特定する - 幅を確認する:8bit、16bit、32bitなど、何桁のフィールドかを特定する
- 解釈を確認する:符号付き整数、符号なし整数、UTF-8、命令、アドレスなど、何のデータかを特定する
- 必要な部分だけ変換する:2進数と16進数なら4bitごと、10進数なら桁の重みで分解・再構成する
変換式より先にこの3点を確認しないと、計算だけ合って意味を取り違えることがあります。2進数と16進数をマスターするとは、数字を素早く変換するだけでなく、どの規則で、そのbit列を読んでいるのかを説明できることです。
まとめ:2進数と16進数は、同じbit列を見るための表記
memo.txtのAは、UnicodeでU+0041に対応し、UTF-8では1byteの値0x41へ符号化されます。その同じbyteを2進数で書けば01000001です。2進数から10進数へは桁の重みを足し、10進数から2進数へは値を2のべき乗へ分解し、16進数とは4bitずつ置き換えます。この「値は同じ、表記と解釈は別」という軸を持てば、ログやダンプで見かけるbit列を暗記ではなく根拠から読めます。
