工学じじいの縁側日記

工学じじいの縁側日記

引退間際の工学じじいがきままに、プログラミングやデバイス、工学について呟きます。

第3回 チキチキ プログラミング初心者のための型に関する考察 ~pythonで基数変換してみるよ~

第3回 チキチキ プログラミング初心者のための型に関する考察 ~pythonで基数変換してみるよ~

f:id:gomta777:20191028111136j:plain



はじめに

1回目で、型ってなんだ、2回目で整数型の中身と正負の整数のメモリ表現を見てみました。

ko-gaku-jiji.hatenablog.jp
ko-gaku-jiji.hatenablog.jp

2回目でも紹介した通り、pythonでは、組み込み関数binを使うことによって、整数を2進数に変換することができます。
また、表示のみだけであればprint関数の書式設定で何とかなります。

ですが、

num = int(input())
print(bin(num))
print(bin(-num))

これで、入力を150とすると

150
0b10010110
-0b10010110

0bは2進数表記であることを表します。なので、150を表すビット列は10010110となります。pythonの型にはCやjavaの型(=メモリサイズ+表現形式の決まり)と違って決まったサイズがありませんので、150をそのまま2進数に変換した10010110が表示されています。
これが例えば、CやJavaのshort型だったとすると

メモリサイズ
short 16ビット(2byte)
int 32ビット(4byte)

なので、
150(10進数) => 0000000011010110(2進数)
となります。
pythonは、これが普通の世代から見るとなんか気持ち悪いw)

そして負の数のビット表現は2の補数を取って、

10進数 2進数
150 0000000011010110
-150 1111111100101010

となるはずですが、まさかの、-0b10010110となります。

よろしい、ならば自作関数だ

10進数と2進数を変換する関数を作る

引数に10進数と整数n(2<=n<10)を与えると、それをn進数に変換して返す関数

def base_conv(number, n):

を考えます。
与えられたnumをn進数へ変換するには、前回説明した通り

ko-gaku-jiji.hatenablog.jp

  • num>0の間以下を繰り返す。
  1. 与えられた整数numをnで割って、商q、余りrを記録
  2. num=q

記録したrを逆順に並べるとn進数で表現されたnumとなります。

pythonで表現してみる

上の処理をpythonで書きます。

ちゃんと動いてそうですね!
実行結果もあってそうな気がします。

2,8,16進数のはなし

コンピュータの世界では、2の乗数と、それに関連して8の倍数を使うことが多いです。
例えば、

8bit = 1byte
1024(=2^10) bit = 1Mbit

0と1の2進数の世界でデータや命令ができているので自然といえば自然なことですね。
また、2進数でデータを表記していると、どんどん長くなってしまいます。

0011101001010110(2進数)
035126(8進数)
3A56(16進数)

2進数だと16bit必要だったものが8進数だと7桁、16進数だと4桁で済んでしまいました。
また、2進数、8進数、16進数は2の乗数が基数となっているため比較的簡単に相互変換可能です。なので、結構多くのプログラミング言語に2進数、8進数、16進数表記が使える機能が備わっています。

ちなみにn進数は0~(nー1)の数字を使って表すというルールがありました。(8進数なら0~7の数字で表される)
16進数を考えてみるとちょっと面白いことになります。

f:id:gomta777:20191112232742p:plain
図:16進変換

3797(10進数)を16で割っていって余りを下から並べると、

14 13 5

となってしまします。1桁あたりに2つの数字が入ってしまいます。
そりゃそうですよね。16進数なんで0~15の数字を一けたに使うんだもん。でも、このままだと、繋げて書いたときに、14135と区別がつきません。困りました。
っということで、16進数の10~15の数字が出てきてしまったときは、桁で使える数字(0~9)は売り切れてもうないのでアルファベットを使います。

10 A
11 B
12 C
13 D
14 E
15 F

3797(10進数)=> 14 13 5(16進数)=> ED5(16進数)

10進数を2,8,16進数に変換する関数

10進数の数字と、基数(2,8,16)を入力すると指定された基数に変換して返す関数を作ります。
2,8,16以外の基数が指定されたときは、Noneを返します。

print(base_conv(3797, 16))
を試すと、ちゃんと
ED5(16)
が表示されます。

そして負の数

(bin)だと2の補数に変換できないよねってこと

はじめに書いたとおり、組み込み関数bin()で整数をビット列に変換できます。しかしながら、負の数には対応しておらず、2の補数が表示されるわけではありません。これは、pythonが動的に型を扱う言語であることの特徴であって、別に欠点であるわけでも、バグでもありません。
ただし、静的に型を扱う言語は多数あり、型についての知識も大事かなと思います。という理念のもとに、型についてグダグダと語ってきました(笑)
話を戻して、2進数の負の数を2の補数で表示してみたい!という願望をかなえる関数を書いてみます。

型=メモリサイズ+表現方法

でしたね。JavaやC言語では、整数型はメモリサイズによってchar(byte)、short、int、longなどと名前が変わっていきます。

ちょっと中身で比べてみます。
pythonで、
print(bin(3))
を実行すると
0b11
が表示されます。

しかし、例えばC言語では
char c=3;
とすると中身は
00000011
となります。
short c=3;
だと、
0000000000000011
です。
charは8ビット、shortは16ビットの固定幅でデータが用意されるというわけです。

これを、C言語の場合で負の数(-3)にしてみます。
char c=-3の場合:
00000011 (元の2進数)
11111100 (1の補数(ビット反転))
11111101 (2の補数(1の補数+1)) ← これがー3の8ビット表現

short c=-3野場合:
0000000000000011 (元の2進数)
1111111111111100 (1の補数(ビット反転))
1111111111111101 (2の補数(1の補数+1)) ← これがー3の8ビット表現

となります。pythonの場合は、ビット幅が決まっていないので内部表現はこうなっているとは限らないです。

pythonで表現してみる

今度は、この処理を、関数にしてみます。

def plus_one(binarr):
は、2進数で一番右のビットに1を足した結果を得る関数

def bit_reverse(binarr):
は、すべてのビットをビット反転する関数です。

結果も、ちゃんと動いているっぽいですね。

所感

pythonで基数変換する処理を書いてみました。
たまには、こういうプログラムも楽しいかもしれない(笑)


↓よかったら押してほしいです!
にほんブログ村 科学ブログ コンピュータサイエンスへ
にほんブログ村