プログラミングを始めると、突然こんな値を見かけるようになります。
0xFF
0x1A
#FFFFFF
0x7F
普段の生活ではほとんど使わない「16進数」です。
コンピュータは2進数で動いているはずなのに、なぜプログラムではわざわざ16進数を使うのでしょうか。
理由はシンプルで、
2進数を人間が読みやすい形に短く表現できるから
です。
この記事では、16進数がプログラミングでよく使われる理由を、Byte・RGBカラー・ビット列などの例から整理します。
16進数は0〜9だけでは足りない
普段使っている10進数では、
0 1 2 3 4 5 6 7 8 9
の10種類の数字を使います。
16進数では16種類必要なので、
0 1 2 3 4 5 6 7 8 9 A B C D E F
を使います。
それぞれ、
A = 10
B = 11
C = 12
D = 13
E = 14
F = 15
です。
したがって16進数の、
10
は10進数の10ではありません。
16進数の10は、
16
を意味します。
0xFFは何を意味する?
プログラムでは、16進数であることを示すために、
0x
を付けることがあります。
たとえば、
0xFF
です。
FFを10進数へ変換すると、
F × 16 + F
なので、
15 × 16 + 15
= 255
となります。
つまり、
0xFF = 255
です。
JavaScriptでも確認できます。
console.log(0xFF);
// 255
なぜ255がよく登場するのか
プログラミングでは255という数字をよく見かけます。
理由は、
255 = 2^8 - 1
だからです。
8ビットをすべて1にすると、
11111111
になります。
これを10進数にすると255。
16進数なら、
FF
です。
つまり、
2進数 11111111
16進数 FF
10進数 255
は、すべて同じ値を表しています。
16進数は2進数と相性がいい
16進数が便利な最大の理由は、
16 = 2^4
であることです。
16進数1桁は、ちょうど2進数4桁に対応します。
たとえば、
0 = 0000
1 = 0001
2 = 0010
3 = 0011
4 = 0100
5 = 0101
6 = 0110
7 = 0111
8 = 1000
9 = 1001
A = 1010
B = 1011
C = 1100
D = 1101
E = 1110
F = 1111
です。
そのため、
11111111
という8桁の2進数は、
1111 1111
と4桁ずつ区切って、
F F
つまり、
FF
と簡単に表せます。
32ビットの値を見ると差が大きい
たとえば次の2進数があります。
11001010111111100001001000110100
人間がこのまま読むのはかなり大変です。
4ビットずつ区切ると、
1100 1010 1111 1110 0001 0010 0011 0100
それぞれ16進数へ変換すると、
C A F E 1 2 3 4
なので、
CAFE1234
となります。
これならかなり読みやすくなります。
16進数は、単なる別の数字表記というより、
長いビット列を圧縮して読むための表記
として非常に便利です。
RGBカラーも16進数
Web制作でも16進数は身近に使われています。
CSSでは、
color: #FF0000;
のような色指定があります。
これは、
FF 00 00
と2桁ずつ分けて考えます。
RGBなので、
R = FF = 255
G = 00 = 0
B = 00 = 0
です。
したがって、
#FF0000
は赤になります。
白なら、
#FFFFFF
なので、
R = 255
G = 255
B = 255
です。
黒なら、
#000000
となります。
16進数2桁で1Byteを表せる
1Byteは8bitです。
そして16進数1桁は4bitなので、
16進数2桁 = 8bit = 1Byte
になります。
これが非常に重要です。
たとえば、
3A
FF
00
7C
は、それぞれ1Byteの値として扱えます。
そのためバイナリデータやHEXダンプを見ると、
48 65 6C 6C 6F
のように2桁ずつ区切られていることがよくあります。
これは、
48
65
6C
6C
6F
という5Byteのデータです。
メモリやバイナリ解析でも16進数が便利
メモリアドレスやバイナリファイルでも16進数がよく使われます。
たとえば、
0x7FFDF000
のような値です。
これを2進数で表示すると非常に長くなります。
10進数でもコンピュータ内部のビット構造との対応が分かりにくくなります。
16進数なら、
1桁 = 4bit
2桁 = 1Byte
という対応関係があるため、ビット構造を確認しながら読みやすいという利点があります。
2進数から16進数へ変換するコツ
たとえば、
10110110
を16進数へ変換します。
右から4桁ずつ分けます。
1011 0110
それぞれ、
1011 = B
0110 = 6
なので、
B6
です。
つまり、
10110110₂ = B6₁₆
となります。
16進数から2進数ならさらに簡単
逆方向も同じです。
たとえば、
3F
なら、
3 = 0011
F = 1111
なので、
00111111
です。
16進数と2進数の変換は、10進数を経由しなくても4bit単位で直接行えます。
10進数から16進数へ変換する
10進数255を16進数へ変換してみます。
255を16で割ると、
255 ÷ 16 = 15 余り15
15は16進数ではFです。
商の15もFなので、
FF
となります。
もう一つ、
26
なら、
26 ÷ 16 = 1 余り10
余り10はAなので、
1A
です。
したがって、
26₁₀ = 1A₁₆
となります。
JavaScriptでは簡単に変換できる
JavaScriptではtoString()を使って進数を指定できます。
10進数255を16進数へ変換するなら、
const value = 255;
console.log(value.toString(16));
// ff
大文字にしたい場合は、
console.log(value.toString(16).toUpperCase());
// FF
です。
2進数なら、
console.log(value.toString(2));
// 11111111
となります。
逆に文字列の16進数を10進数として解釈するなら、
console.log(parseInt("FF", 16));
// 255
です。
進数を跨いで確認すると理解しやすい
2進数・8進数・10進数・16進数を何度も手計算すると、桁数が増えたときにミスしやすくなります。
値の対応をまとめて確認したい場合は、進数を相互変換するツールを使うと、同じ値を複数の進数で比較できます。
特に2進数と16進数を並べて見ると、
4bit = 16進数1桁
8bit = 16進数2桁
という関係が視覚的にも分かりやすくなります。
16進数は「難しい数字」ではない
最初は、
A
B
C
D
E
F
が数字として使われることに違和感があります。
しかしプログラミングでは、
2進数 = コンピュータに近い表現
16進数 = 人間が読みやすくした2進数
くらいに考えると理解しやすくなります。
特に、
F = 1111
FF = 11111111
という対応を覚えると、ビット演算やバイナリデータもかなり読みやすくなります。
まとめ
プログラミングで16進数がよく使われる大きな理由は、
16 = 2^4
だからです。
16進数1桁が2進数4桁に対応するため、
長い2進数を短く表現できる
というメリットがあります。
そして、
2桁の16進数 = 8bit = 1Byte
という非常に扱いやすい関係があります。
そのため16進数は、
- RGBカラー
- バイナリデータ
- メモリアドレス
- ビット演算
- HEXダンプ
- 通信データ
など、さまざまな場面で使われています。
「16進数を覚える」というより、
2進数を読みやすくするための表現
として理解すると、プログラミングで登場する理由が分かりやすくなります。