char | 8bit整数(文字型) |
short int | 16bit整数 |
long int | 32bit整数 |
int | 32bit整数 |
のような数値がある。 これは現在最も普及しているシステムでの 数値であり、これとは異なる場合もある。
また、整数型には符号無し整数と符号付き整数の2種類がある。C言語で言えば
unsigned int a; signed int b;のようにunsignedまたはsignedを 付加でき、unsignedならば0または正の数を、 signedは正負の数を表現できる。 何も付けない場合はsigned扱いに なる。
bit pattern | 数値 |
---|---|
11111111111111111111111111111111 | 4294967295 |
... | ... |
00000000000000000000000000000010 | 2 |
00000000000000000000000000000001 | 1 |
00000000000000000000000000000000 | 0 |
つまり普通の2進数であり、32個の各bitが次のような重みを持っている。
231 | 230 | 229 | ..... | 21 | 20 |
上記のように、nビットで 0~2n-1の範囲の数を表現できる。 すなわち、Cのunsignedな整数型は、
char | 0~255 |
short int | 0~65535 |
long int | 0~4294967295 |
の範囲の数が表せることになる。
bit pattern | 数値 |
---|---|
01111111111111111111111111111111 | 2147483647 |
... | ... |
00000000000000000000000000000010 | 2 |
00000000000000000000000000000001 | 1 |
00000000000000000000000000000000 | 0 |
11111111111111111111111111111111 | -1 |
11111111111111111111111111111110 | -2 |
... | ... |
10000000000000000000000000000000 | -2147483648 |
このような負の数の表現形式を「2の補数形式」と呼ぶ。 32個の各bitが次のような重みを持っていると考えることもできる (先頭だけ重みが負になっている)。
-231 | 230 | 229 | ..... | 21 | 20 |
上記のように、2の補数形式の場合、nビットで -2n-1~2n-1-1の範囲の数を表現できる。 すなわち、Cのsignedな整数型は、
char | -128~127 |
short int | -32768~32767 |
long int | -2147483648~2147483647 |
の範囲の数が表せることになる。このようなフォーマットを採用している理由は、 オーバーフローを無視したunsigned用の32bit加算器を、そのままsigned用の 加算器として使えることが大きな理由として挙げられる。
浮動小数点数は仮数部の長さ、指数部の長さ、指数が2か10か16か、など、 様々なバリエーションが考えられ、実際昔は計算機メーカー毎に様々な フォーマットが乱立していた。 そこで、1985年にWilliam Kahanが中心となって
IEEE 754: Standard for Binary Floating-Point Arithmeticという標準規格が制定された。幸いなことにこれ以降に世に出たハードウェアの ほぼ全てがこの規格に従うこととなった。 以下、IEEE 754 Std.のフォーマットについて説明する。 倍精度(C言語で言うところのdouble)、単精度(C言語で言うところのfloat)の 2種類が規格化されている。
1(符号) | 11(指数部) | 52(仮数部) |
x = (-1)s × 1.m × 2e-1023のような式で書ける。
例えば、5.25は2進数で書くと
であるから、計算機内では、101.01 = 1.0101×22
0 | 1000000001 | 0101000000000000000000000000000000000000000000000000 |
のように格納されている。指数部の「1000000001」は、「2+1023=1025」 を2進数にしたものである。
ここまでは比較的簡単であるが、IEEE 754 Std.には複雑な例外事項がある。 以下それについて説明する。 指数部eは11bit符号無しなので0から2047まで取れるが、 その両端の数 (0と2047) は特殊な数を表すのに使われていて、 上で説明した規則が使われるのは指数部が1から2046まで (1023を減じた実際の指数では-1022から1023まで) の間である。
x = (-1)s × 0.m × 2-1022のような数値を表す。赤字のところの違いに注意。
まとめると、次の表のようになる。
- | m = 0 | m ≠ 0 |
e = 0 | ±0 | x = (-1)s × 0.m × 2-1022 (非正規化数) |
1 ≤ e ≤ 2046 | x = (-1)s × 1.m × 2e-1023 (正規化数) | |
e = 2047 | ±∞ | NaN (Not a Number) |
1(符号) | 8(指数部) | 23(仮数部) |
のようになっている。指数部のオフセットが1023から127になるなどの違いは あるが、ほとんど倍精度と同じ。表にまとめると次の通り。
- | m = 0 | m ≠ 0 |
e = 0 | ±0 | x = (-1)s × 0.m × 2-126 (非正規化数) |
1 ≤ e ≤ 254 | x = (-1)s × 1.m × 2e-127 (正規化数) | |
e = 255 | ±∞ | NaN (Not a Number) |
1001100110011001100110011001100110011001100110011001 | 10011001100.... |
となる。はみ出た部分の先頭が「1」なので、0捨1入で繰り上げる。最終的には、 計算機内では、
0 | 01111111011 | 1001100110011001100110011001100110011001100110011010 |
のように格納されている。すなわち、10進数の0.1は計算機には正確に格納できず、少しだけ0.1より大きい値で格納されていることが分かる。
一般に、10進数における四捨五入、2進数における0捨1入は、 切り捨てと切り上げのうち近い方(誤差が小さい方)に丸めるという 分かりやすいポリシーに基づいている。 しかし、例えば2進数の10.1(10進数の2.5)を整数に丸めることを考えると、 切り捨てた10(10進数の2)と切り上げた11(10進数の3)との距離はともに1/2で等しいが、 0捨1入では切り上げが選ばれることになる。
この「上下との距離が等しい」場合に、必ず切り上げるのではなく 「偶数丸め」と呼ばれるルールに基づいて切り捨てか切り上げかを選ぶと よいことが知られており、IEEE754でもそのルールに従って丸めを 行うことになっている。 偶数丸めルールは、 切り捨てた結果と切り上げた結果の仮数部の末尾を見比べて、 偶数である方を選ぶというルールである。 切り捨てた結果と切り上げた結果の仮数部の末尾は1違いなので、必ずどちらかは 偶数でどちらかは奇数である。 2進数では仮数部の末尾は0か1なので、0の方に丸める。
例えば、倍精度で1+2-53の計算結果がどうなるか考えてみる。 この値は2進数で
1.00000000000000000000000000000000000000000000000000001で全長54bitなので、倍精度には収まりきらない。この数を切り上げた数と 切り捨てた数はそれぞれ
1.0000000000000000000000000000000000000000000000000001 1.0000000000000000000000000000000000000000000000000000であり、丸めによって生じる誤差はどちらも同じ2-53であるが、 末尾が0である切り捨てが行われることになる。
(1より大きな最小の浮動小数点数)-1で定義される。 IEEE 754 Std.においては、