2019年2月1日金曜日

開発環境

プログラマの数学第2版 (結城 浩 (著)、SBクリエイティブ)の第1章(ゼロの物語 - 「ない」ものが「ある」ことの意味)、2進法が出てくるのは、コンピュータで2進法が使われている理由で詳しい記述があったけど、位取り記数法の位取り記数法とは何かに、

プログラミングでは、8進法や16進法もよく使われます。
とあるものの、何故8進法や16進法がよく使われるかの記述が見当たらなかったので、16進数について何故使われるかを視覚的に表してみた。

8進数については、ファイルのパーミッション等の変更の時に使われるのをみたことはあるけど、実際に自身では使ったことがほとんどないので使用例のみ。ファイルのパーミッション等の変更の時は8進数による絶対モード指定ではなく、文字列によるモードの指定を使うから。

コード

Python 3

#!/usr/bin/env python3

print('Hello, world!')

入出力結果(cmd(コマンドプロンプト)、Terminal、Jupyter(IPython))

$ ls -l sample.py
-rw-r--r--  1 kamimura  staff  47  2  1 11:55 sample.py
$ ./sample.py 
bash: ./sample.py: Permission denied
$ chmod 0755 sample.py
$ ls -l sample.py
-rwxr-xr-x  1 kamimura  staff  47  2  1 11:55 sample.py*
$ ./sample.py 
Hello, world!
$ chmod 0644 sample.py
$ ls -l sample.py
-rw-r--r--  1 kamimura  staff  47  2  1 11:55 sample.py
$ ./sample.py
bash: ./sample.py: Permission denied
$ chmod a+x sample.py
$ ls -l sample.py
-rwxr-xr-x  1 kamimura  staff  47  2  1 11:55 sample.py*
$ ./sample.py 
Hello, world!
$ chmod a-x sample.py
$ ls -l sample.py
-rw-r--r--  1 kamimura  staff  47  2  1 11:55 sample.py
$ ./sample.py 
bash: ./sample.py: Permission denied
$ 

上記のコードでしていることは、sample.pyファイルに全てのユーザーが実行可能な権限を与えたり、その権限を削除したり。

文字列によるモードの指定は全てのユーザー(allの頭文字)に実行権限(executeのx)を与える(+)、削除する(-)ということで、「a+x」、「a-x」と指定。

4桁の8進数の先頭の0についての説明は省略。残り3桁について、左から全てのユーザー、グループのユーザー、その他のユーザーと割り当てる。ここで、各ユーザーの権限について、読み込み可能(r)、書き込み可能(w)、実行可能(x)の3種類ある。ここで3桁の2進数を考える。左から順に、各権限について可能ならば1、不可能ならば0とすれば、各ユーザーが読み込み、書き込み、実行可能な場合は111、読み書きのみ可能なのは110、読み込みと実行可能がな場合は101、読み込みのみ可能な場合は100となる、これを8進数に変換すると、それぞれ7、6、5、4となる。よって、「a+x」と等価なのは、0755、「a-x」と等価なのは0644となる。

8進数による指定より、文字列による指定の方が分かりやすいから、これが8進法をほとんど使うことがない理由。(プログラミングの他の分野等で8進数がよく使われる分野がもしかしたらあるのかも。)

8進数と違って16進数をよく使う理由を視覚化。前回定義した関数を利用。

コード

Python 3

#!/usr/bin/env python3
def to_bin(n: int) -> str:
    '''
    >>> to_bin(12)
    '1100'
    '''
    ''' 10進数を2進数に変換 '''
    b = ''
    while n != 0:
        n, r = divmod(n, 2)
        b = f'{r}{b}'
    if b == '':
        return '0'
    return b


def from_bin(bin: str) -> int:
    '''
    >>> from_bin('1100')
    12
    '''
    ''' 2進数を10進数に変換 '''
    n = 0
    for c in bin:
        n *= 2
        n += int(c)
    return n

# 一般化


def to_n(num: int, n: int) -> str:
    '''
    >>> to_n(12, 2)
    '1100'
    >>> to_n(12, 8)
    '14'
    >>> to_n(12, 16)
    'c'
    '''
    ''' 10進数をn進数に変換 '''
    m = ''
    while num != 0:
        num, r = divmod(num, n)
        if r >= 10:
            r -= 10
            m = f'{chr(ord("a") + r)}{m}'
        else:
            m = f'{r}{m}'
    if m == '':
        return '0'
    return m


def from_n(s: str, n: int) -> int:
    '''
    >>> from_n('1100', 2)
    12
    >>> from_n('14', 8)
    12
    >>> from_n('c', 16)
    12
    '''
    ''' n進数を10進数に変換 '''
    m = 0
    for c in s:
        m *= n
        if c >= 'a':
            m += 10 + ord(c) - ord('a')
        else:
            m += int(c)
    return m


def n_to_m(s: str, n: int, m: int) -> str:
    '''
    >>> n_to_m('1100', 2, 8)
    '14'
    >>> n_to_m('1100', 2, 16)
    'c'
    >>> n_to_m('14', 8, 2)
    '1100'
    >>> n_to_m('c', 16, 2)
    '1100'
    '''
    ''' n進数をm進数に変換 '''
    return to_n(from_n(s, n), m)


if __name__ == '__main__':
    import doctest
    doctest.testmod()

    for n in range(2 ** 8):
        b = to_bin(n)
        h = n_to_m(b, 2, 16)
        print(f'{n:3} {b:0>8} {h:0>2}')

入出力結果(cmd(コマンドプロンプト)、Terminal、Jupyter(IPython))

$ python3 sample.py
  0 00000000 00
  1 00000001 01
  2 00000010 02
  3 00000011 03
  4 00000100 04
  5 00000101 05
  6 00000110 06
  7 00000111 07
  8 00001000 08
  9 00001001 09
 10 00001010 0a
 11 00001011 0b
 12 00001100 0c
 13 00001101 0d
 14 00001110 0e
 15 00001111 0f
 16 00010000 10
 17 00010001 11
 18 00010010 12
 19 00010011 13
 20 00010100 14
 21 00010101 15
 22 00010110 16
 23 00010111 17
 24 00011000 18
 25 00011001 19
 26 00011010 1a
 27 00011011 1b
 28 00011100 1c
 29 00011101 1d
 30 00011110 1e
 31 00011111 1f
 32 00100000 20
 33 00100001 21
 34 00100010 22
 35 00100011 23
 36 00100100 24
 37 00100101 25
 38 00100110 26
 39 00100111 27
 40 00101000 28
 41 00101001 29
 42 00101010 2a
 43 00101011 2b
 44 00101100 2c
 45 00101101 2d
 46 00101110 2e
 47 00101111 2f
 48 00110000 30
 49 00110001 31
 50 00110010 32
 51 00110011 33
 52 00110100 34
 53 00110101 35
 54 00110110 36
 55 00110111 37
 56 00111000 38
 57 00111001 39
 58 00111010 3a
 59 00111011 3b
 60 00111100 3c
 61 00111101 3d
 62 00111110 3e
 63 00111111 3f
 64 01000000 40
 65 01000001 41
 66 01000010 42
 67 01000011 43
 68 01000100 44
 69 01000101 45
 70 01000110 46
 71 01000111 47
 72 01001000 48
 73 01001001 49
 74 01001010 4a
 75 01001011 4b
 76 01001100 4c
 77 01001101 4d
 78 01001110 4e
 79 01001111 4f
 80 01010000 50
 81 01010001 51
 82 01010010 52
 83 01010011 53
 84 01010100 54
 85 01010101 55
 86 01010110 56
 87 01010111 57
 88 01011000 58
 89 01011001 59
 90 01011010 5a
 91 01011011 5b
 92 01011100 5c
 93 01011101 5d
 94 01011110 5e
 95 01011111 5f
 96 01100000 60
 97 01100001 61
 98 01100010 62
 99 01100011 63
100 01100100 64
101 01100101 65
102 01100110 66
103 01100111 67
104 01101000 68
105 01101001 69
106 01101010 6a
107 01101011 6b
108 01101100 6c
109 01101101 6d
110 01101110 6e
111 01101111 6f
112 01110000 70
113 01110001 71
114 01110010 72
115 01110011 73
116 01110100 74
117 01110101 75
118 01110110 76
119 01110111 77
120 01111000 78
121 01111001 79
122 01111010 7a
123 01111011 7b
124 01111100 7c
125 01111101 7d
126 01111110 7e
127 01111111 7f
128 10000000 80
129 10000001 81
130 10000010 82
131 10000011 83
132 10000100 84
133 10000101 85
134 10000110 86
135 10000111 87
136 10001000 88
137 10001001 89
138 10001010 8a
139 10001011 8b
140 10001100 8c
141 10001101 8d
142 10001110 8e
143 10001111 8f
144 10010000 90
145 10010001 91
146 10010010 92
147 10010011 93
148 10010100 94
149 10010101 95
150 10010110 96
151 10010111 97
152 10011000 98
153 10011001 99
154 10011010 9a
155 10011011 9b
156 10011100 9c
157 10011101 9d
158 10011110 9e
159 10011111 9f
160 10100000 a0
161 10100001 a1
162 10100010 a2
163 10100011 a3
164 10100100 a4
165 10100101 a5
166 10100110 a6
167 10100111 a7
168 10101000 a8
169 10101001 a9
170 10101010 aa
171 10101011 ab
172 10101100 ac
173 10101101 ad
174 10101110 ae
175 10101111 af
176 10110000 b0
177 10110001 b1
178 10110010 b2
179 10110011 b3
180 10110100 b4
181 10110101 b5
182 10110110 b6
183 10110111 b7
184 10111000 b8
185 10111001 b9
186 10111010 ba
187 10111011 bb
188 10111100 bc
189 10111101 bd
190 10111110 be
191 10111111 bf
192 11000000 c0
193 11000001 c1
194 11000010 c2
195 11000011 c3
196 11000100 c4
197 11000101 c5
198 11000110 c6
199 11000111 c7
200 11001000 c8
201 11001001 c9
202 11001010 ca
203 11001011 cb
204 11001100 cc
205 11001101 cd
206 11001110 ce
207 11001111 cf
208 11010000 d0
209 11010001 d1
210 11010010 d2
211 11010011 d3
212 11010100 d4
213 11010101 d5
214 11010110 d6
215 11010111 d7
216 11011000 d8
217 11011001 d9
218 11011010 da
219 11011011 db
220 11011100 dc
221 11011101 dd
222 11011110 de
223 11011111 df
224 11100000 e0
225 11100001 e1
226 11100010 e2
227 11100011 e3
228 11100100 e4
229 11100101 e5
230 11100110 e6
231 11100111 e7
232 11101000 e8
233 11101001 e9
234 11101010 ea
235 11101011 eb
236 11101100 ec
237 11101101 ed
238 11101110 ee
239 11101111 ef
240 11110000 f0
241 11110001 f1
242 11110010 f2
243 11110011 f3
244 11110100 f4
245 11110101 f5
246 11110110 f6
247 11110111 f7
248 11111000 f8
249 11111001 f9
250 11111010 fa
251 11111011 fb
252 11111100 fc
253 11111101 fd
254 11111110 fe
255 11111111 ff
$

左から順に10進数、2進数、16進数表記。2進数をよく使う理由は本著を読んでもらうとして、16進数を使う理由は、2進数(4ビット)を1文字で表すことができ、見やすくなるから。例えば、10進数の10を2進数で表すと「1010」。16進数で表すと「a」。桁数が多くなるほど、ぱっと見では2進数の場合、1と0の個数の数え間違えが起きそうだけど、16進数では2進数よりは把握しやすい、あるいは短く書くことができることが分かる。

ということで、このことが16進数をプログラミングでよく使う理由の1つだったり。

0 コメント:

コメントを投稿