2進数は怖くない!

2021.09.17

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

(Click here for English version)

こんにちは、ドイツのモナでございます〜

仕事や趣味によって、2進数が日常的に多かれ少なれ現れたりします。サブネットマスク計算などのためのツールがあるので自分で計算しなくても済むのですが、最初から2進数を理解しようとしない人が少なくはないと思います。この記事で2進数の怖いイメージが理解の邪魔にならないように簡単に説明したいと思います!一つの注意点として、数学をなるべく避けるようにする、あるいはできるだけ簡単でわかりやすく説明するようにしますので、数学の背景のある方には以下の説明があまり勉強にならない可能性があります。

なお、IT関連の仕事では多くの計算がバイト基準で十分なので、8ビットまでの数字を主に例として使います。

ちょっとした数学の復習

数学が苦手な方向けの記事ですが、使わざるを得ない用語などを簡単に説明します。

本記事では以下のように用語を使っていきます。

  • 自然数
    • 0以上の数えるために使う数字のことです。例:3、15、144などは自然数でありますが、1/3、 0.5、-4は自然数ではありません。
  • 累乗
    • 指数(上図参照)が自然数であり、(上図参照)と底の掛け算の繰り返しの計算で得た結果のことです。繰り返しの回数は、指数で表示されます。例:25は2×2×2×2×2という形式にでも書けます。

2進数とは

日常生活にて使われている数字は10進数であります、つまり0〜9という十の記号からできている数字です。考えてみると、想像できる数字なら全てがその記号の組み合わせとなりますね。なお、数字は2進数としても表すことができます、つまり0と1というたった二つの記号しか使わない表し方です。2進数とは日常的に使っている数字を少なくした記号で書く方法です。それは特に0と1の言語しかわからないコンピューターにぴったりですね!

なおこの記事では自然数、つまり0以上の数字だけに目を向けます。

2進数と10進数の比較

注意:この記事の全ての2進数はカッコ、また末尾に付いている小さな2で書かれています。例えば、(101)2は2進数の5であり、10進数の101ではありません。

2進数の計算などは基本的に10進数の計算と変わりません。唯一の違いは桁毎に利用する数字(記号)だけです。2進数を理解するには、まず10進数のあり方を詳しく見てみましょう。

全ての10進数はその桁に分割できます。例えば、144という数字を見てみましょう。1、4、4という3桁として見ることができます。その桁が実際の数字の中のポジションによって、10の累乗の倍数を表示します。どういうことかというと、144は「(1 × 100) + (4 × 10) + (4 × 1)」, またはさらに短く「100 + 40 + 4」として表現することができます(下図参照)。100また10は明らかに10の累乗でありますが(100 = 102, 10 = 101)、覚えていない方もいらっしゃるかもしれないので、1 = 100ということで、1も10の累乗であります。任意の数の0乗は常に1であることを覚えておくことおすすめします。明らかですが、10の累乗は常に「1」の後に0個以上の「0」が続く数字であることを覚えておくのも良いでしょう。「0」を数えることで冪数がわかります。例えば、1000 = 103. もちろん逆の方法でもいけます:10n は「1」 の後にn個の「0」が続く数字です、例えば104 = 10000.

さて、これは考えられる全ての数字で機能することを自分自身を確信させてください。どんな時も、各桁の数字は『その桁に対応する10の累乗』の位数と言えます。『その桁に対応する10の累乗』を確かめるには、その桁の後に続く桁の数を数えればいいだけです。

もう一つの例として1607を取り上げましょう。この数字は「(1 × 1000) + (6 × 100) + (0 × 10) + (7 × 1)」に分割できますね。1 の後に続く桁の数は3なので、1に掛ける10の累乗は1000 = 103; 6 の後に続く桁の数は2なので、6に掛ける10 の累乗は100 = 102, 7 の後に続く桁は3なので、7に掛ける10 の累乗は1 = 100.

2進数の場合も全く同じ考えです!

ここで、まず2進数では「1」の後に0個以上の「0」が続く場合の値は常に2の累乗であることを理解することが必要です。繰り返しになりますが、2進数の末尾の「0」を数えることで、冪数がわかります。例えば、(1000)2 の末尾は、「0」が3個なので、23 = 8.

これも全ての2進数で同じように機能します。2進数の(101)2を見てみましょう。1、0、1という桁に分割できますが、10進数の例みたいに「(1 × (100)2) + (0 × (10)2) + (1 × (1)2)」という形式で書けます。なおこの例でいうと、(100)2 = 22 = 4 、また (1)2 = 20 = 1。結果は 4 + 1 = 5。

これまで見てきたことは、2進数は実際に10進数と同じように機能するということですが、唯一の違いは1桁あたりに使用される数字(記号)が少ないことです。この点を踏まえて、2進数ではどのように計算を行うのか見てみましょう。

10進数に1を足すと、2つの異なる結果が考えらえます:

  1. 最後の桁が9未満の場合、その最後の桁が1増えた数字になります(例:15 + 1 = 16)
  2. 最後の桁が9の場合、その桁が0になり、一つ前の桁が1増えた数字になります(例:139 + 1 = 140)

これによって1が加えられた桁については、その桁が9であったか9未満であったかに応じて、これら2つのルールが同じように適用されることにご注意ください。(例:199 + 1 = 200) 従って、全桁が「9」だけの数字に1を足すと、「1」の後に「0」ばかりが続くことになり、もちろん10の累乗になります(例:99+1=100)。つまり、全桁が「9」の10進数は10 の累乗から1を引いた数字ということです。それは、10進数の1桁の値として9が最大であるからです。「9」を数えることで冪数がわかります、例えば99は「9」が2個なので、99 = 102 - 1。

さて、2進数はどうでしょう?なお、各桁の最大値は1ですね。2進数に1を足すと、また2つの異なる結果が考えらえます:

  1. 最後の桁が1未満、つまり0の場合、その最後の桁が1増えた数字になります(例:(110)2 + (1)2 = (111)2
  2. 最後の桁が1の場合、その桁が0になり、一つ前の桁が1増えた数字になります(例:(101)2 + (1)2 = (110)2

(11)2 を見てみましょう。1を足すと、(100)2という2の累乗になります。つまり、全桁が「1」の2進数は2の累乗から1を引いた数字ということです。さらに言えば、「1」を数えることでやはり冪数がわかります。例えば、(111)2 の「1」が3個なので、23 - 1 = 8 - 1 = 7.

2進数→10 進数

さて、ある2進数を見たとき、10進数に変換するにはどうすればよいでしょうか。

まずシンプルな計算方法でいきますが、計算を高速化するためのコツなどを以下で紹介します。数字が小さくても大きくても、常に機能する方法です。実は、上記でこの方法を見たばかりですが、ここで一般化した形で書いておきます。

  1. 数字の中に1があるときは、冪数を得るために末尾の桁を数える(0でも1でも構いません)。その数を「n」と呼びましょう。
  2. 2nを算出する
  3. 算出された数値をすべて加算

「1」が2個ある(10010000)2を見てみましょう(下図参照)。上記の方法を使用すると、それぞれの1が「27 = 128」と「24 = 16」ということで、128 + 16 = 144となります。

10進数→2進数

2の累乗を覚えていないとすると、10進数を2進数に変換する最も簡単な方法は、ホーナー法だと思います。

  1. 数字を2で割って(整数除算)、余り(0または1のいずれか) をメモする
  2. 商が0になるまで1.の結果を余りなしで使って1.を繰り返す
  3. 下から上にメモした余りが求めている2進数である

さて、ホーナー法を使って例として144を2進数に変換してみましょう。

  1. 144 / 2 = 72, 余り:0
  2. 72 / 2 = 36, 余り:0
  3. 36 / 2 = 18, 余り:0
  4. 18 / 2 = 9, 余り:0
  5. 9 / 2 = 4, 余り:1
  6. 4 / 2 = 2, 余り:0
  7. 2 / 2 = 1, 余り:0
  8. 1 / 2 = 0, 余り:1

その結果、144 = (10010000)2.

基本コツ

2進数での計算を早くするためにはいろんなコツがあります。

  • 0〜15を暗記する
    • シンプルですがかなり役に立ちます

  • 1024までの2の累乗を暗記する
    • これもまた非常にシンプルなことですが、あらゆる種類の計算に便利です。数字が2の累乗であることを認識するだけでも、とても助かります。「2048」といゲームはこの点で非常に役に立ちます。

  • 末尾に0をつけると2倍になるということです
    • 例:(11)2 -> (110)2 は3×2 = 6
  • 末尾に1をつけると2を掛けて1を足すということです
    • 例:(101)2 -> (1011)2 は5×2+ 1 = 11ということです
  • n桁の2進数の補数とは、すべての桁を反転させたもので、つまりすべての1が0に、すべての0が1になります。この2つの数字(元の数字とその補数)を足すと、「1」だけでできているn桁の2進数となります。その数字は2n - 1の値を持ちます。
  • 210 ≈ 1000(正確には1024)
  • 232 ≈ 40億
  • 手を使って2進数の練習ができます! 指を下にすると0、指を上にすると1になります。片手で0から31まで、両手で1023まで表現できます。

バイト(8ビット)専用のコツ

  • バイトで考える
    • バイトベースの計算が多いので、8桁の2進数で計算の練習しておけば、よくあるパターンをパッと見でわかることも増えますし、計算も楽になります。
  • バイト内のすべての位置で2の累乗を覚えておく(下図参照)
  • 右4桁が1桁の2の累乗、つまり1、2、4、そして8。このコツは16以上の2の冪を早く見つけるには便利です。

 

ぱっと見でわかる特徴

  • 全桁が1:2の累乗から1を引いた数字
  • 一つの「1」だけ:2の累乗
  • 下一桁が1:奇数
  • バイトの場合:255以上の値になることはない

実例

では、実際に2進数が使われている例を見てみましょう。

chmod

chmodは、Unix系システムにおいて、ファイルやディレクトリの権限を設定するシェルコマンドです。権利を与える順番は、Read(読む) - Write(書く) - Execute(実行)です。それぞれの権利は、0(拒否)と1(許可)で表されます。

つまり、例えば全権は111で表されたら、2進数の7となります。また、読み取りと実行はできるが、書き込みはできないという場合は、101(2進数の5)となります。これらの権限は、ファイルオーナ、ユーザーグループ、その他の人の順に設定する必要があります。例えば、ユーザーには全権限を与え、ユーザーグループとそれ以外の人には読み取りと実行の権限だけを与えたい(つまり、ユーザーだけがファイルを変更できるということ)とすると、chmod 755 some_file.txt を使ってできます。

chmodの数字が何を意味しているのか疑問に思っていた人は、これでわかったと幸いです!

subnet masks

IPアドレスは4つのオクテットをドットで分割したもので、192.168.0.1のように構成されています。各オクテットは1バイトに格納されるので、IPアドレス全体は4バイトの長さとなります。

1バイトは8ビット(=先頭の0を含む8桁の2進数)であるため、1つのバイトが取り得る値は0~255です。したがって、IPアドレスの各オクテットは、最大で255の値を持つことができす。最大値になるには、すべてのビットを1にする必要があります。

サブネットマスクは通常192.168.0.1/24のように表されます。

ここで重要なのは/24です。これは、24ビットがこのIPアドレスのネットワーク部分として使用されることを意味しています。つまり、最初の24ビットが1にされているので、ドット表記で書かれたマスクは255.255.255.0となります。

最も簡単なサブネットマスクは/8/16/24、そして/32で、これらの数字は8の倍数だからです。それぞれに対応するサブネットマスクは、「255」のオクテットの後に「0」のオクテットが続くだけです。

なお、それ以外の長さのサブネットマスクの場合は、8で割った後の余りを見ればよいのです。例えばサブネットマスクが/28の場合、28を8で割れば結果が3となり、余りが4となるので、最初のオクテットが255で、4オクテット目の最初のビットが1になっている(11110000)ということです。どの方法で計算するかは好みですが、128 + 64 + 32 + 16を計算するか、あるいは、その数字の補数である(00001111)の8 + 4 + 2 + 1を計算して255から引くという両方の方法のどちらでも結果は240です。個人的には後者の方が好きです。なぜなら、常に1だけで構成される数字(先頭の0は無視する)なので、2の累乗から1を引いた数字になるからです。上記のコツを使えばその計算がかなり楽です。

全てのサブネットマスクは「1」の後に0以上の「0」が続く文字列です。つまり、例えばワイルドカードマスクなどに使われる補数は、常に2の累乗から1を引いたものになるということです。さらには、サブネットマスクの値が非常に限られていることという意味もしてます。IPアドレス全体ではなく、1オクテットのみを考慮すると、これらの値は以下の通りです。

  • 255 (11111111)
  • 254 (11111110)(基本的には使用しないが、可能ではある)
  • 252 (11111100)
  • 248 (11111000)
  • 240 (11110000)
  • 224 (11100000)
  • 192 (11000000)
  • 128 (10000000)
  • 0     (00000000)

おまけ:16進数

コンピュータサイエンスやITの分野で使われているもう一つの数字のシステムは、16進法です。これも10進法や2進法と同じ仕組みですが、0〜9の数字とA〜Fの文字の16種類の記号を使用します。

2進数から16進数に変換するには、右から4桁のブロックを取り、対応する16進数の値に変換すればよいだけです。桁数が4で割り切れない場合は、先頭の0で埋めればよいです。2進数の(10010000)2 を (1001)2 = 9 、 (0000)2 = 0というブロックに分割できるので、2進数の(10010000)2 は16進数で(90)16 となります。

16進数から2進数に変換するには、16進数の各桁を4桁の2進数に変換すればいいだけです。

16進数の(A3)16を(A)16 = (1010)2、(3)16 = (0011)2という4桁の2進数に分割できるので、16進数の(A3)16は2進数の(10100011)2となります。

それは、2進数の4桁で24 = 16種類の数字を表すことができて、16進方では1桁で表せるからです。

終わりに

2進数は一見して怖そうに見えるかもしれませんが、実は日常的に使っている10進数と同じように使えます。この記事が、今まで2進数に否定的だった人が、少しでも理解を深める助けになれば幸いです。