2進数と16進数

Rubyでのプログラミングではあまり計算機の中の都合を意識しなくてよい ようになっている。しかし、凝ったプログラムを作ろうとしたり、 Ruby以外で少し複雑な計算などを行なわせようとするとき、あるいは とくにコンピュータグラフィックスなどの基礎知識を習得したい場合には、 計算機の内部構造、とくに2進数の性質を知らないとスムーズに 理解できないことがある。しっかりと覚えておきたい。

2進数

計算機の内部では、数値も文字も画像も全て 1 と 0 の2進数 の状態で保存している。 人間にとって分かりやすい10進数でなく2進数が使われるのは、 計算機が何かを記憶するための格納場所であるメモリに 書き込むためには電気的にONかOFF (あるいは磁気的 にNかS)かの形で書くことになるからである。これを何個も組み合わせて一つのデー タ格納領域とする。たとえば、ONとOFFの箱を8個用意して、次のようなデータを 入れる。

OFFONOFFOFF OFFOFFOFFON

これは模式的な絵であるが、計算機のメモリの中では、 電気がONになっていたり、磁石がN極を向いている状態を想像するのが実状に近い。 このOFFとONの状態を2進数にしよう。

0100 0001

2進数の 01000001 になる。2進数を10進数に直すときは、 各桁に以下のような倍率を掛けたものを足して行けばよい。

128倍64倍32倍16倍 8倍4倍2倍1倍
0100 0001

これは、64×1 + 1×1 = 65 となる。

16進数

いくら計算機が内部で2進数を使うとはいえ、2進数のまま数値を書いたので は長くて読みづらい。かといって、10進数に直すのは計算が面倒である。そのた め、2進数4桁をまとめて16進数にして数値を表記することが多い。

たとえば、10進数でもまとめて表記することがある。長さの尺度が「メート ル」のまま距離を表現したら大変なので、10進数3桁(10の3乗=1000)ごとにまと めて「キロ」メートルで表現することがある。逆に、10の-3乗メートルを「ミリ」 として表記したりする。また、金額を表記するときも3桁毎にまとめて 2,500,000円 のように10の3乗ごとにまとめてあたかも1000進法のようにして表 すことがある。このように何桁かをまとめて単位を付けたりすることは日常的に も身近なことである。

2進数4桁をまとめると、2**4=16個の数を表すことができる。

2進数 16進数 10進数
000000
000111
001022
001133
010044
010155
011066
011177
100088
100199
1010a10
1011b11
1100c12
1101d13
1110e14
1111f15

ONとOFFの箱で出てきた、

OFFONOFFOFF OFFOFFOFFON

つまり

0100 0001

は、2進数で 01000001 だが、これを16進数で表すと4桁毎に変換すればよいので

となる。2進数 01000001 は、16進数で41である。 ちなみに41は文字コードでいえば、アルファベッ ト大文字Aに該当する。つまり、Aという文字を計算機の内部に記憶させたいとき は

0100 0001

のようにデータが格納されることになる。

16進数と10進数の変換

16進数を10進数へ

16進数を10進数に変換するには、各桁に16の倍数を掛けたものを足す。 たとえば、16進数 56ce を10進数に直してみよう。

4096倍
(16**3)
256倍
(16**2)
16倍1倍
56ce

cの桁は10進数で12を意味することと、eの桁は10進数で14を意味 することに注意して計算すると、

4096*5 + 256*6 + 16*12 + 14 = 22222

となる。

10進数を16進数へ

10進数を16進数に変換するには、

  1. 10進数を直接16進数に変換する
  2. 10進数を2進数に変換してから16進数に変換する

の2とおりの方法がある。

  1. 10進数を直接16進数に変換する

    元の10進数を割れなくなるまで16で割り、 割ったときの余りを逆に拾って行けばよい。たとえば、10進数64206を16進数に 直してみよう。

    16) 64206    (余り)
    16)  4012 …… 14 → e
    16)   250 …… 12 → c
    16)    15 …… 10 → a
    16)     0 …… 15 → f  ↑下から上に読む
    

    割り算の余りを下から順に読んだ、0xface が16進の値となる。

  2. 10進数を2進数に変換してから16進数に変換する

    元の10進数を2進数に変換する。

    2)  64206  (余り)
    2)  32103 …… 0
    2)  16051 …… 1
    2)   8025 …… 1
    2)   4012 …… 1
    2)   2006 …… 0
    2)   1003 …… 0
    2)    501 …… 1
    2)    250 …… 1
    2)    125 …… 0
    2)     62 …… 1
    2)     31 …… 0
    2)     15 …… 1
    2)      7 …… 1
    2)      3 …… 1
    2)      1 …… 1
    2)      0 …… 1   ↑ 下から上に読む
    

余りを下から上に読むと、

1111101011001110

となるが、これを1の位(右側)から4桁毎に区切る。

              ← 右から4桁毎に区切る
1111,1010,1100,1110

区切った2進数を16進数に直していく。

1111,1010,1100,1110
 ↑   ↑   ↑   ↑
 f    a    c    e   (16進数)

Rubyプログラムでの扱い

Rubyプログラムでは、プログラム中に書く数値の先頭に付ける記号(接頭辞)で 何進法かを指示できる。

接頭辞 意味
なし 10進数
0b 2進数
0 8進数
0x 16進数

0b1010 は、2進数の1010 (10進数でいえば10を)表すことになり、0x10f0 は、16進数の 10F0 (10進数でいえば4336)を表すことになる。

プログラム中に、0b1010と書こうが 10 と書こ うが、0xa と書こうが、一度読み込まれれば計算機の内部ではど れも10進数でいうところの10として扱われ、どれも全く同じである。これを出力 するときに何進法にするかは printf の%つきフォーマットで選 択することができる。

以下に、printf の%フォーマットで 数値を何進法で出すかの意味を示す。

%フォーマット 何進法で出力されるか
%b 2進法
%o 8進法
%d 10進法
%x 16進法

これを利用すると、16進数を10進数に直したり、その逆をしたり することをRubyプログラムに任せることができる。

printf("16進の%xは10進で%dです\n", 0x555, 0x555)
↓
16進の555は10進で1365です
printf("10進の%dは2進で%bです\n", 2002, 2002)
↓
10進の2002は2進で11111010010です

本日の目次