情報基礎、という授業があります。
これは工学部のほとんどの学科で、クラス指定科目として”履修を強く勧める科目”になっていて、普通は受けることになっています。
しかし、話を聞いていると学科によって内容が様々で、
工業化学の人はVirtual Box上でLinux動かしているというし、
物理工学科の人はPCつかってメール送ったりしてるといいます。
そして電気電子工学科は何をしているかといえば、
まさかのまさか、ほか学科で必ず利用しているPCすら使わず、ひたすらPCの内部の動きや情報伝達についての講義。
僕はある程度習う前から知識がある…はずなのですが、それでもちんぷんかんぷんな部分もかなり多いです。(そっちのほうが多い)
教員から教える気は全く伝わってこないので、自分でやれってことだと思うのですが、全くわからない人も多いんじゃないでしょうか。
というわけで今回は僕のメモも兼ねて、調べながら、僕の今までの専門分野に近い(?)2進数の話をしようと思います。
授業で話していないことも含みますが、知ると理解が深まっていいと思うことを書きます。
レベルが高いような低いような、中途半端な文章を書いてしまったので、存在意義が微妙な感じになってしまいました・・・
意識高い人向けに書いているのに、丁寧に書こうとしていたら、丁寧さが意識高くない人に説明するレベルになってしまいました・・・
<説明のための表記>
基本的にプログラムやTeXにおいてのルールに従いながら説明します。
プログラムにおいて、何進数かを区別するときに、数字の前に2文字接頭辞(?)をつけます。
ここでも、説明のため、そのルールに従います。
2進数を表記するときは0b~の
16進数を表記するときは0x~です。
何も書かれていないときは10進数です。
また、16進数表記において大文字/小文字は区別しません。
8ビットのデータにおいて
0b11110000
上なら1111の部分を上位ビット、0000の部分を下位ビットと言います。
一番右の0を0ビット目、順に左に1つずつ数字を足していって、一番左の1を7ビット目と言います。
5^2は5の2乗を表します。(Pythonでは5**2)
3*5は3かける5を表します。
3/5は3わる5を表します。
<2進数のメリット>
2進数は10進数と比べると桁数も多く、パッと見ではどんなぐらいの大きさなのか全くわからず、デメリットばかりのようにも見えます。
でも、メリットも大きいです。
たとえば自転車のオートライトについて考えてみると、
明るさセンサーがある値を下回った時にライトを付ける、という動作をします。
ここで、センサーから値を入力するときには大きく分けて2つの方法があります。
一つがアナログ(的)入力、つまり明るさセンサーの値を明るさに応じて8ビットなら0~255のあいだの値を取得するという方法です。
でもこれだと無駄が多い。というのも、必要なのは明るさがある値よりも高いか低いか、という事だけなので、高いなら0、低いなら1、というように1ビットで取得することができるからです。
これだとセンサーが8こついていた場合も、8ビット送信するだけで、必要な情報をすべて伝えることができます。この場合、センサー1,3,5が反応していた時のデータは2進数表記で0b00101010となりますが、これだと1,3,5が反応している、とすぐに分かります。これを10進数にすると全く何を言っているデータなのかわからなくなります。
<2進数と16進数>
2進数のメリットが大きいとはいえ、やはり桁数が多くなります。
そこで便利なのが16進数です。
16=2^4であることからもわかるのですが、
2進数は4桁ごとに16進数一文字に変換することができます。
(16) (2)
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
上のテーブルと見比べながら文意を理解して貰いたいですが、
例えば
0xAC=0b10101100
0x3F=0b00111111
になります。
このテーブルを覚えてしまうと2-16の変換が何桁になっても一瞬で終わらせられるようになります。
<ビットシフト>
右や左に2進数の数字をずらすことをビットシフトと言います。
数字を扱う上で、2進数を左にひとつずらすことは、数を二倍することを意味します。2ビットずらせば4倍になります。
(余談ですが、2倍するよりも左にひとつずらすほうが処理が高速なため、かなり一般的に使われる手法です。)
ずらしたときに新しく必要な桁はすべて0になります。
例
0b11001001<<2 →0b00100100
<数字の型>
ふつうは、数字をコンピュータ上に蓄えるとき、その数字がどんな数であるか(=型といいます)、を始めに登録します。
(signed) int,short,longなどの型の数字は+-を含む数字を保存することができ、
unsigned int,short,longなどの型の数字は+のみを保存することができます。
float,doubleという型の数字は小数点以下も含んだ数を表現することができます。
<unsigned型>
unsigned 〜は最も単純な数値表現です。
通常の2進数表記で、
xyz=(2^2)*x+(2^1)*y+(2^0)*z
という意味になります。
8ビットならば0〜255の256(2^8)通りの数字を表現することができます。
<signed型>
負の数字まで表現できる形式です。符号付き、とも言います。
前知識として、
2進数の足し算をする時、8bitsという条件のもとで計算をすると、
11001011
+11000000
110001011
ですが、一番上の桁は無視され(8bitsを超える)、10001011になります。
これを考慮に入れた上で、
11111111に1を足すと、0になることがわかります。
これを-1(1を足すと0になる数)として捉えよう、というのがsigned型です。
同様に11111110が-2、のようになるのですが、
ここで問題になるのが、どこまでを正の数と考え、どこまでを負の数と考えるか、です。
10000000を128と捉えるのか、-128と捉えるのか、です。
普通は真ん中で区切りますよね。初めて考えた人も普通のセンスを持った方のようで、真ん中で区切ってくれたようです。
この時、-なら最上位ビットが1に、+なら0になることがわかります(考えてみて!)
だから、最上位ビットを符号とよんだりします。
この法則に従えば、10000000は-128と捉えるほうがわかりやすいです。
よって、-128〜127までの256通りの数を表現できるようになります。
でも、-nを考えるときにいちいち100000000からn引くのは大変ですよね。
これを考える方法があって授業プリントに書いてあります。
調べればすぐに出てくるのでここでは書きません。
<float型=浮動小数点型>
flaot型は普通は32ビット用います。
その32bitは3つの部分に分けることができ、
0~22bit目までの23bitsを仮数部
23~30bit目までの8bitsを指数部
31bit目を符号
と言います。
0 01111111 10000000000000000000000
符号 指数部 仮数部
符号の部分は1ならば-、0ならば+を表します。
指数部は符号付きの2進数です。
仮数や指数の考え方としては
例えば25.75という数があった時に、これを2進小数として表記すれば、
11001.11となります。(16+8+1+1/2+1/4)
これはつまり、1.100111を左に4bitsずらしたもの、すなわち1.100111に2^4=16倍したものであるので、
1.100111*(2^4)とも表すことができます。
※なぜ0.1100111*(2^5)としないかは後述
つまりこの時
仮数は1.100111
指数は4
です。
それでやってしまいたくなるのが
0 00000100 11001110000000000000000 (+ 4 1100111)
ですが、
IEEE方式では、仮数部には仮数-1、指数部には指数+127した数を入れる、ということになっています。この時仮数が1より大きくないとひけないため、仮数が1より大きくなるように調整しました。また、仮数部には小数点以下しか書きません。(0なはずですが)
よって、25.75をfloat表記すると、(本当は間隔をあけずにつなげて書きます)
0 10000011 10011100000000000000000
になります。
IEEE方式での規定はおそらく、
指数部が5の方が-100よりも大きい、ということをわかりやすくするためでしょう。
<おわりに>
論理演算も書きたかったのですが、長さ的にちょっと・・・
あとこういうのは結構自分でプログラムを書いてみて、確かめてみないと理解しづらいと思います。これを期にやってみても面白いと思います!
参考資料
http://htp365.blog61.fc2.com/blog-entry-12.html
http://d.hatena.ne.jp/waidotto/20110716/1310823016