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
<メニューに戻る>