プログラミング言語

コンピュータ内部での数値表現

26 コメント
views
0 フォロー

コンピュータ内部ではどのようにして数値を表現しているか。

とくに
作成: 2020/07/16 (木) 08:34:02
通報 ...
1
とくに 2020/07/16 (木) 08:36:13

一般的にコンピュータ内部では二進数が用いられる。
二進数とは0と1のみで数値を表す方法である。
人間が普段扱っているのは十進数で、0から9までの十通りで表している。

2
とくに 2020/07/16 (木) 08:39:03

十進数で昇順に数える時、0,1,2...9と数えるが、9までくるともう使える数字が無いので桁が繰り上がり10になる。
二進数でも同様で0,1と数えてもう使える数字が無いので桁が繰り上がり、10になる。

3
とくに 2020/07/16 (木) 08:40:18

ちなみに二進数では桁の重みを考慮した読み方はせず、10は「じゅう」ではなく「いちぜろ」の様に読む。

4
とくに 2020/07/16 (木) 08:44:42

以下、十進数と二進数の対応表

十進数二進数
00
11
210
311
4100
5101
6110
7111
81000
91001
101010
111011
121100
131101
141110
151111
5
とくに 2020/07/16 (木) 08:46:45

二進数は一桁を「ビット」と呼ぶことがある。
1は1ビット、1001は4ビット。
8ビットで1バイトとなる。

6
とくに 2020/07/16 (木) 09:07:49 修正

十進数から二進数の変換
十進数から二進数に変換するには次の方法を使う。

方法その1
十進数の数値をdとする。
dを2で割る。この時の商をd1とする。更にその時の余りをm1とする。
d1を更に2で割る。この時の商をd2とする。更にその時の余りをm2とする。
以下、商が0か1になるまで繰り返し。
その後一番最後に出した余り(商が1の場合は最後に算出した商)から最初の余りまでを繋げるとdを二進数表記したものになっている。

方法その2
十進数の数値をdとする。
dをd >= 2nを満たす最大の数(xとする。)で引く。二進数の桁の重みxに1を立てて、dをxで引いた差をd1とする。
nを一つ減らす。
d >= 2nを満たさない場合は、二進数の桁の重みxに0を置いてdをそのままd1とする。
nを一つ減らす。
以下差が0か1になるまで繰り返し。
後は最初から重みxの桁に置いたものを繋げるとdを二進数表記した数になっている。

分かりづら。

7
とくに 2020/07/16 (木) 09:18:40

その前にこっちを書いた方が良かった。
二進数の桁の重みは左から20,21,22となっている。

2726252423222120
01001101
8
とくに 2020/07/16 (木) 09:32:12

例として25を二進数に変換してみる。

方法その1
25 / 2 = 12 ... 1
12 / 2 = 6 ... 0
6 / 2 = 3 ... 0
3 / 2 = 1 ... 1
よって25を二進数にすると、11001になる。

9
とくに 2020/07/16 (木) 09:39:46 修正

方法その2

25 - 16 = 924の位置は1
9 - 8 = 123の位置は1
1 - 4 = -322の位置は0
1 - 2 = -121の位置は0
1 - 1 = 020の位置は1

よって25は二進数で11001となる

10
とくに 2020/07/16 (木) 15:33:45

逆の二進数から十進数に変換する方法。

二進数で表された数値をbとする。
bのnビット目が1であったら2nを加算する。
これを全てのビットが1であるものに対して行う。
2nで加算していった合計の数値が十進数でbを表した数になっている。

11
とくに 2020/07/16 (木) 15:50:02 修正

例として1011010を十進数に変換する。

どちら側からでも良いが、取り敢えず右の最下位ビットから見ていく。
20は0なので0。
21は1なので2。
22は0なので2のまま。
23は1なので2に23を足して10。
24は1なので10に24を足して26。
25は0なので26のまま。
26は1なので26に26を足して90。

よって1011010は十進数で90となる。

12
とくに 2020/07/16 (木) 16:08:09

負数の表現
十進数では負数は数値の前に-記号を付けて表すが、二進数などの他の進数では通常そのような負数の表し方はしない。
例えば-23を二進数表記にすると、11101001になる。(8ビットで表す場合)
これには決まりがあり、最上位ビットが1ならその数は負数と見做すと言うのがある。
最上位ビットの値を定める必要がある為、予め表現するビット数を決めておく必要がある。

13
とくに 2020/07/16 (木) 16:14:58

先ほどの-23を二進数表記にするには以下の手順で行う。なお、今回は8ビットで表現するものとする。
-23の絶対値をとる→23
23を二進数に変換→00010111(8ビットで表現する為今回は先頭に0を補間)
00010111をビット反転する(1の補数)→11101000
11101000に1を加算する。(2の補数)→11101001
11101001が-23を二進数で表した結果になる。

14
とくに 2020/07/16 (木) 16:30:04

多くのプログラミング言語において、数値型は表現できる数字の範囲が決まっている。
これは予め何ビットで表現するかを決めてあるからである。
例えばC言語などのint型は処理系によって異なるが、32ビットで表されることが多い。
int型は符号付きの整数(負数を扱える整数)なので-231~231-1までの範囲を扱える。
これは-2,147,483,648から2,147,483,647までの範囲である。
unsigned intは符号なしの整数(負数を扱えない整数)なので0~232-1(0~4,294,967,295)までの範囲になる。

15
とくに 2020/07/16 (木) 16:39:56

扱える範囲を超える場合、オーバフローとなる。
8ビットで考えると、
符号付きの場合、-27~27-1までの範囲(-128~127)なので、この符号付き8ビットの数値で128を表そうとすると1000 0000となってしまい、-128として扱われてしまう。
符号なしとして考えた場合、扱える範囲は0~28-1(0~255)である。
256を表そうとすると、1 0000 0000となるが、8ビットまでしか表せないので最上位のビットは切り捨てられ、0000 0000となってしまう。

17
とくに 2020/07/16 (木) 17:00:53 >> 15

符号なし整数で負数を表そうとすると当然負数は扱えないので、全て正の整数として扱われる。
例えば符号なし8ビットの場合、-1を表そうとすると1111 1111となるが、最上位ビットは符号として解釈されない為、-1ではなく、255として扱われる。

16
とくに 2020/07/16 (木) 16:50:40

プログラミングではこうした特性により思わぬバグを生んでしまうことがある。
例えば、

unsigned int i;
for(i = 1000;i >= 0;i--){
 //なんか処理
}

上記のforループは無限ループとなってしまう。
これはiが符号なしである為、負数を扱えない。よってiが0の時に0から1を引いた結果がint型で表せる最大の正の整数になってしまうからである。これでは条件のi >= 0を満たすことができず無限ループとなる。

18
とくに 2020/07/16 (木) 19:00:08

コンピュータ内部で小数を表す方法は色々あるが、主に使われているのはIEEE 754と言う規格である。
IEEE 754は浮動小数点をコンピュータ内部で表すための規格。
コンピュータ内部で小数を表す時に多くの場合、この規格が使われる。
表現するビット数は32ビットと64ビットが一般的。

19
とくに 2020/07/16 (木) 19:04:55

32ビットの場合

符号指数部仮数部
00111110001000000000000000000000

最上位ビットは符号として扱われる。
23ビット目から30ビット目は指数部で8ビット。
0ビット目から22ビット目は仮数部で23ビット。

負号は正の値なら0、負の値なら1。
指数は8ビットで表し、実際の値に127を加えた値にする。これは指数部が負の時にも対応しやすくする為。127をバイアス値と言う。
仮数部は1から始まるように指数部を調整し小数点を移動する。一番左は必ず1なので無視する。

20
とくに 2020/07/16 (木) 19:08:20

例として3.375を浮動小数点にすることを考える。表現方法は32ビットの単精度浮動小数点とする。

整数部と少数部の3と0.375に分ける。

3を二進数に変換→11
0.375を二進数に変換→011
(小数点以下の場合は小数第一位から順に2-1、2-2、2-3・・・となる)
0.375 - 0.5 = -0.125 → 0
0.375 - 0.25 = 0.125 → 1
0.125 - 0.125 = 0 → 1

3.375は二進数で11.011となる。
しかし、これは固定小数なので浮動小数点に変換する。

11.011 → 1.1011 * 21
ここで仮数部の一番左の1は無視する。

1011

後は23ビットになるように0で埋める

10110000000000000000000

指数部は1なのでこれに127を足す。

1 + 127 = 128

128を二進数に変換。

10000000

符号は正の値なので0

よってIEEE754の形式にすると

01000000010110000000000000000000

となる。

21
とくに 2020/07/16 (木) 19:15:58

倍精度の場合、指数部が11ビット、仮数部が52ビット、バイアス値が1023になる。
それ以外は単精度と同じ。

22
とくに 2020/07/16 (木) 19:55:05

二進数では十進数で表現できる小数でも無限小数になることがある。
例えば0.1などは二進数にすると無限小数になる。

0.1 - 0.5 = -0.4 → 0
0.1 - 0.25 =  -0.15 → 0
0.1 - 0.125 = -0.025 → 0
0.1 - 0.0625 = 0.0375 → 1
0.0375 - 0.03125 = 0.00625 → 1
0.00625 - 0.015625 = -0.009375 → 0
0.00625 - 0.0078125 = -0.0015625 → 0
0.00625 - 0.00390625 = 0.00234375 → 1
以下、無限に続く。

0.00011001100...

これを単精度で表す。
1.1001100... * 2-4

指数部は

-4 + 127 = 123
二進数に変換
01111011

符号は0

00111101110011001100110011001100...

このままでは入りきらないので値を丸める必要がある。
IEEE754では丸め方が様々あるが、デフォルトでは最近接偶数丸めと言うものである。
0は切り捨て、1は切り上げる。

00111101110011001100110011001100 | 1

00111101110011001100110011001101

23
とくに 2020/07/16 (木) 21:40:06

その他にも8進数や16進数などがある。
8進数は0~7までの数字を使って数値を表す。
16進数は0~9の数字、更にA~Fのアルファベットを使って表す。

24
とくに 2020/07/16 (木) 21:42:19

二進数との対応は八進数の一桁が3ビットに対応、十六進数の一桁が4ビットに対応する。

25
とくに 2020/07/16 (木) 21:58:27
二進数 0 100 101 000 111 101
八進数 0 4 5 0 7 5
二進数 0100 1010 0011 1101
十六進数 4 A 3 D
26
とくに 2020/07/16 (木) 22:04:05

八進数から十進数、十六進数から十進数への変換は二進数からの変換と要領は同じ。逆も然り。