2進数と16進法

1.10進数から2進数へ

10進数を2進数に変換する関数を作ります。

ExcelやCalcですと、DEC2BIN()という関数があり、これで10進数を2進数に変換できます。

しかし、大きな数は変換できません。511までです。

8ビット(1バイト)の2進数では、10進数の0から255まで(負でない整数の場合)まで表せます。

16ビットでは10進数の0から65535までです。現在のコンピュータは64ビットでの計算ができるようになっていますが、まずは16ビットくらいまでは変換できるようにしましょう。CindyScriptを使ってそのための関数を作ります。

10進数を2進数に変換するには、正の整数の場合、2で次々に割りながら、その余りをビットとして立てていくのでした。このことは、単に手続きとして暗記するのではなく、位取り記数法の原理として理解しておきましょう。

では、10進数を2進数に変換する関数を作ります。関数定義はInitializationスロットに書くとよいでしょう。

次のようなスクリプトにしてみました。(行番号は実際には不要です)

--------------------------------------------------------------------------------------------------------

 1 :  bin=["0","1"];

 2 : 

 3 :  dec2bin(n):=(

 4 :    keta=if(n>255,16,8);

 5 :    binstr="";

 6 :    repeat(keta,

 7 :      binstr=bin_(mod(n,2)+1)+binstr;

 8 :       n=floor(n/2);

 9:     );

10:    binstr;

11: );

--------------------------------------------------------------------------------------------------------

まず1行目で、2進数の各桁で使う文字、0と1をリストにしておきます。

3行目が関数の名前。引数はnです。ここで、nは正の整数であるという前提です。そうでない場合はどうするかはあとで考えましょう。

4行目、引数のnの大きさにより、8ビットで表すか16ビットで表すかを決めます。この数は、6行目の繰り返し数にもなります。

5行目、結果を表す文字列です。最初は空。

6行目から9行目が計算。繰り返しです。

7行目、nを2で割った余り mod(n) を求めます。0か1ですので、1を足してbinリストから該当する文字を取り出します。たし算になっていますが、文字列のたし算は文字の追加になります。

8行目、nを2で割った商をあらためてnとします。floorという関数は床関数というもので、その数を超えない最大の整数を求めるものです。

数学の教科書ではガウス記号で表されるものです。floorはCindyScriptに用意されている関数です。

9行目、繰り返します。途中でnは0になることがありますが、かまわず繰り返します。それによって8ビットもしくは16ビットの表示ができるわけです。

10行目、戻り値です。CindyScriptの関数定義では、最後に評価された(計算された)結果が戻り値となります。ここでは何も計算していませんが

binstrを「評価」したので、binstr が戻り値となります。この行がないと、戻り値は8行目のnになってしまいます。

これで関数定義ができたので、Drawスロットに書いて実行してみましょう。

2.10進法から16進数へ

10進数を16進数に変換する関数を作ります。原理は2進数の場合と同じですから、先ほどのスクリプトを少し書き換えればいいですね。

最初に16進数で使う文字0からFまでのリストを作っておきます。16進数なのでこんどは hex とします。

hex=["0","1","2","3","4","5","6","7","8","9","A","B","C","D","E","F"];

あとは、255以下なら2桁表示、256以上なら4桁表示とし、2で割るところを16で割るようにすればいいだけです。こんな結果になります。

3.負の数へ拡張

ExcelやCalcのDEC2HEX関数は、負の整数も変換します。−512までです。つまり、ExcelやCalcでは、2進数への変換については、10ビットまでできるということになります。ただ、プログラミングの観点からすると、10ビットというのは中途半端なので、ここでも8ビットまたは16ビットとしましょう。

さて、8ビットで正の数と負の数を表すためには、まず、「正か負か」という情報が必要になり、そのために最上位(一番左)の1ビットを使います。ここが0なら正、1なら負、とします。残り7ビットで数を表すので、正の数は127まで表せます。次に負の数ですが、「絶対値が同じ正の数と負の数の和は0」ということに着目します。さらに、2進数では、1+1=10 と「1たす1」で桁が上がって0になる、ことも考慮すると

0001+1111=10000

となって、はみだした一番左の1を除けば4ビット分は0になります。すなわち、−1を4ビットで表すと1111です。このような表現方法を「2の補数」というのですが、この「補数」というのがわかりにくい言葉で、これでめげてしまう人も結構います。教科書によっては「各ビットを反転させた1の補数に1を加える」という説明がしてあるのもあり、「補数」でますますわけがわからなくなる人もいるでしょう。

「補数」はパスして、「1111(8ビットなら11111111)が −1でそこから1ずつ引いていく(−2は1110、−3は1101・・・)」と考えたり、「とにかく足して 0000・・・になるようにする」と考えたり、そこは人によって覚えやすい方法をとればよいでしょう。

では、スクリプトを書くには、どのアルゴリズムを使えばよいでしょう。

「各ビットを反転させた1の補数に1を加える」の「1を加える」がヒントになります。実際の表記を眺めてみましょう。簡単のため4ビットにします。

10進  −5  −4  −3  −2   −1   0 1 2 3 4 5

2進 1011 1100 1101 1110 1111 0000 0001 0010 0011 0100 0101  

どうでしょう。「1を加えた数」を比べると、すべてのビットが反転していることに気がつきましたか。「1を引くと」でもいいですよ。

「−4に1を加えて−3。符号を変えた3のビットを反転させれば−4の2進表記」になっていますね。

逆に、「−4の符号を変えて4,1を引いて3,3のビットを反転」でもいいでしょう。

次のようなスクリプトにしてみました。関数名は、符号付ということでsgnをつけました。

--------------------------------------------------------------------------------------------------------

 1 : bin=["0","1"];

 2 : 

 3 : dec2binsgn(n):=(

 4 :  keta=if(or(n>127,n<-128),16,8);

 5 :  if(n<0,bin=reverse(bin);

 6 :           wn=abs(n)-1,

 7 :           wn=abs(n);

 8 :  );

 9 :  binstr="";

10:   repeat(keta,

11:     binstr=bin_(mod(wn,2)+1)+binstr;

12:      wn=floor(wn/2);

13:   );

14:   binstr;

15: );

--------------------------------------------------------------------------------------------------------

4行目 表示桁数を決めます。

5行目 nが負の数だったら、binの順序を入れ替えて bin=["1","0"] にするのです。つまりビット反転。

6行目 5行目の続きです。wnを設定します。「nの符号を変えて1を引く」

7行目 nが負でなければ、wnはそのままnを代入します。

8行目から後は先ほどと同じ。次々に2で割っていきます。

4.10進小数を2進小数へ

まず、小数の表現について。10進数で考えたものをそのまま2進数に適用します。

たとえば、 0.8125 を2進数で表すと 0.8125=0.5+0.25+0.0625  なので 0.1101 となります。

これを機械的にやっていくには、つぎのように、次々に2をかけながら、その整数部分をとっていきます。

これを使ってスクリプトを書きます。ただし、整数の場合と違って、いつまでたっても終わらないことがあります。無限小数になるのです。そこで、どこかで打ち切ることにしますが、16ビットまでいったら打ち切ることにしましょう。そのためには、repeat文での繰り返しではなくwhile文での繰り返しを行ないます。整数の場合と違って少し複雑です。

--------------------------------------------------------------------------------------------------------

 1 :  bin=["0","1"];

 2 : 

 3 :  decimal2bin(x):=(

 4 :    if(x>=1,binstr="換算する数は1未満の小数です",

 5 :      binstr="0.";

 6 :      keta=0;

 7 :      while(and(x!=0,keta<16),

 8 :        wx=x*2;

 9 :        binstr=binstr+bin_(floor(wx)+1);

10 :        x=wx-floor(wx);

11:       keta=keta+1;

12:      );

13:    if(and(keta==16,x!=0),binstr=binstr+"・・以下さらに続きます");

14:    );

15:    binstr;

16:  );

--------------------------------------------------------------------------------------------------------

3行目 引数はxです。

4行目 xが1以上だと2をかけたときに整数部分が2以上になり、9行目でエラーになります。もともと小数の変換をする関数ですのでここでエラーメッセージを出すようにします。そうでなければ5行目にいくので、行末がコンマになっていることに注意してください。

5行目から xが1未満であればここから14行目までが実行されます。binstrは小数なのでまず 0. にします。

6行目 整数の場合と異なり、桁数ははじめに決めるのではなく、カウントをしていきます。はじめは0

7行目 while(A,B) はAが成り立つ間Bを実行する、という繰り返し構文です。and(C,D)は、CかつDです。

終了するのは、2倍していって整数部分を取り除き、最後に0になったときです。ですから、xが0でない(!=0)あいだ繰り返します。

もうひとつ、桁数が16桁未満なら繰り返します。この2つの条件が両方とも成り立つときなので、and です。

8行目 xに2をかけたものをwxとします。

9行目 wxが1より大きくなったら1を、そうでなければ0を取り出すのですが、それは床関数 floor を使えばできます。

floor(wx) は0か1なので、0ならば、bin=["0","1"]の1番目、1ならば2番目を取り出して、binstrの右に付け加えます。

10行目 wxから0か1を引いてxとします。

11行目 何桁作ったかカウントします。

13行目 16桁までやって終わらない場合、「さらに続きます」という文字を付け加えます。

次が実行結果です。

0.1 は2進数では無限小数になるのです。

このことは、コンピュータにおける計算誤差の問題に直接関わってきます。

5. 浮動小数点

実数を表すには浮動小数点法が用いられます。

32ビットで表す場合(単精度という),一番左が符号ビット,次の8ビットが指数,そのあとが仮数です。

指数,仮数というのは,たとえば10進数で 123456.78 を 1.2345678×10の5乗 と表したとき,5が指数,1.2345678が仮数です。

これと同じことを2進法で行うわけです。

スクリプトは簡単ではないのでここでは省略します。

この他,10進数,2進数,16進数の相互変換をする「基数変換」をWeb上で実行できます。

http://userweb.pep.ne.jp/hannyalab/practiceh/kisuuhenkan.html

メニューに戻る