パソコン活用研究C&C++究め道
Unsigned型と浮動小数点型の内部構造
変数のデータ型についての話は、一番おもしろくないものだと思いますが、画面出力&キーボード入力&変数のところでは、データ型についてこまいかいことはふれなかったので、unsighned型と浮動小数点型(実数型)などについて少し補足しておきたいと思います。
1 unsigned型
コンピュータは2進数で数値を扱っていますが、負の数はどうやって表すのでしょうか。整数型(short,
int, long)および文字型(char)には符号付と符号無し(unsigned型)のふたつのパターンがあります。符号無しの場合は、表現できるのは正の数だけで、負の数を表すことができません。他方、符号付の場合は、データの最上位ビットを符号用に使い、負(マイナス)も表現できるようにしています。
char型とshort型の一般的な例を以下に示します。
char型
符号なし(unsigned型)
ビット |
7 |
6 |
5 |
4 |
3 |
2 |
1 |
0 |
内部構造 |
<--データ --> |
表現できる値
&H00 〜 &HFF
0 〜 255
00000000 〜 11111111
符号付
ビット |
7 |
6 |
5 |
4 |
3 |
2 |
1 |
0 |
内部構造 |
符号
0 正
1 負 |
<--データ --> |
表現できる値
&H00 〜 &H7F &H80 〜 &HFF
0 〜 127 -128 〜 -1
00000000 〜 01111111 10000000 〜 11111111
short型
符号なし
ビット |
15 |
14 |
13 |
12 |
11 |
10 |
9 |
8 |
7 |
6 |
5 |
4 |
3 |
2 |
1 |
0 |
内部構造 |
<---- データ -----> |
表現できる値
&H0000 〜 &HFFFF
0 〜 65535
符号あり
ビット |
15 |
14 |
13 |
12 |
11 |
10 |
9 |
8 |
7 |
6 |
5 |
4 |
3 |
2 |
1 |
0 |
内部構造 |
符号
0 正
1 負 |
<---- データ -----> |
表現できる値
&H0000 〜 &HfFFF &H8000 〜 &HFFFF
0 〜 32767 -32768 〜 -1
コンピュータ上で負の数を表す方法にはいくつかあり、ここで説明したのは2の補数方式
という方法です。2の補数方式について、負の数の表し方(インターネット&PC120%活用)で
説明しています。
実際のプログラムによる符号付き型と符号なし型の挙動の違いは
Unsigned型(符号なし型)と符号付き型のテストを参照下さい。
2 浮動小数点型
浮動小数点型(実数型)の場合は、一般的には符号付型のみになります。浮動小数点の内部構造の仕様にはいくつかバリエーションがありますが、基本は同じです。ここでは、LSIC86に実装されている浮動小数点型で説明してみます。
LSIC86のマニュアルから浮動小数点型の説明の部分を抜粋してみます。
(マニュアルからの抜粋)
浮動小数点型は、 上位から順に符号ビット、指数部、仮数部の3つのフィールドから構成され、その各ビット数は、上の表中にしるされているとおりです。符号ビットはどの型も1ビットで、0のときは正、1のときは負を表します。 指数部はげたをはいており (biased)、0111...111というビットパターンが指数部0を意味します。仮数部は、float型とdouble型では、先頭の1を省いたケチ表現で、小数点は一番左にあるものとみなされます。long double型では、先頭の1は省略されず、小数点はbit63とbit62の間にあるものとみなされます。
以下に、いくつかの浮動小数点数について各型での内部表現を 16 進数で示します。
float double long double
0 00000000
0000000000000000
00000000000000000000
1 3F800000
3FF0000000000000
3FFF8000000000000000
3 40400000
4008000000000000
4000C000000000000000
-3 C0400000
C008000000000000
C000C000000000000000
0.25 3E800000
3FD0000000000000
3FFD8000000000000000
マニュアルに書いてあることは、浮動小数点について理解している人でないと、何のことかわからないと思いますので、わかりやすく説明してみます。
LSIC86の場合、float型は、以下のように符号を表す最上位ビットに、指数部8ビット、仮数部23ビットの構成をとります。(指数部、仮数部をどう扱うかは、浮動小数点のバリエーションにより異なるので、他のコンパイラでは少し異なるかもしれません。ただし、基本は同じ)
ビット |
31 |
30 |
29 |
28 |
27 |
26 |
25 |
24 |
23 |
22 |
21 |
20 |
19 |
............. |
4 |
3 |
2 |
1 |
0 |
内部構造 |
符号
0 正
1 負 |
指数部 |
仮数部 |
先頭に、1. がつくと仮定して、指数部と仮数部は以下のような役割となって実数を表現します。
±1. [仮数分]×2[指数部]
更に細かくみると、LSIC86では、指数部は7F(16進)の時に0を意味するので、正確には
±1. [仮数分]×2[指数部-7F]
ということになります。
仮数部は、
第22ビットが、1/2 = 0.5
第21ビットが、1/4 = 0.25
第20ビットが、1/8 = 0.125
を意味します。
上記のマニュアルで、1は内部表現で3F800000(16進)になっていますが、どうしてこれが1になるか検証してみましょう。3F800000を2進になすと、以下の通りになります。
3 |
F |
8 |
0 |
0 |
0 |
0 |
0 |
0011 |
1111 |
1000 |
0000 |
0000 |
0000 |
0000 |
0000 |
これを、符号と指数部、仮数部に分解して分析してみます。
ビット |
31 |
30 |
29 |
28 |
27 |
26 |
25 |
24 |
23 |
22 |
21 |
20 |
19 |
............. |
4 |
3 |
2 |
1 |
0 |
内部構造 |
符号
0 正
1 負 |
指数部 |
仮数部 |
|
0 |
01111111(&H7F) |
00000..... 0000000000000000(&H0) |
符号は0なので正の数。
指数部は7Fですが、LSICでは7F-7F = 0になります。仮数部は0です、よって
1. [0]×2[0] = 1.0 × 1 = 1.0
ということになります。
では、もうひとつfloatの内部表現C0400000が-3になるかも検証してみましょう。まず2進に変換してみます。
C |
0 |
4 |
0 |
0 |
0 |
0 |
0 |
1100 |
0000 |
0100 |
0000 |
0000 |
0000 |
0000 |
0000 |
符号、指数部、仮数部に分解します。
ビット |
31 |
30 |
29 |
28 |
27 |
26 |
25 |
24 |
23 |
22 |
21 |
20 |
19 |
............. |
4 |
3 |
2 |
1 |
0 |
内部構造 |
符号
0 正
1 負 |
指数部 |
仮数部 |
|
1 |
10000000(&H80) |
10000..... 0000000000000000 |
符号は1 よって負の数
指数部はLSICでは80-7F = 1
仮数部は0.5
-1. [0.5]×2[1] = -1.5 × 2 = -3.0
では、実数の内部表現を2進数で表すプログラムを作成して、いくつかの実数の内部表現を確かめてみましょう。
以下のプログラムは、シフト演算で、内部表現の各ビットをとりだし表示します。なお、float型(実数型)にはシフト演算が使えないので、short型2個からなるs_tag型を作り、float型と共用体を作ることにより、内部表現を取り出しています。
シフト演算については演算子を、共用体については共用体をご参照下さい。
#include<stdio.h>
struct s_tag {
short s1;
short s2;
};
union u_tag {
struct s_tag s_acc;
float f;
} u_acc;
main()
{
int i, bit;
scanf("%f",&u_acc.f);
printf("\n");
for (i=15;i>=0;i--){
bit=(u_acc.s_acc.s2>>i)&0x01;
printf("%1d",bit);
}
for (i=15;i>=0;i--){
bit=(u_acc.s_acc.s1>>i)&0x01;
printf("%1d",bit);
}
printf("\n");
}
|
以下、実行例です。確かめてみて下さい。
D:\win95\C>float
1.0
00111111100000000000000000000000
D:\win95\C>float
1.0000001
00111111100000000000000000000001
D:\win95\C>float
1.0000002
00111111100000000000000000000010
D:\win95\C>float
1.000001
00111111100000000000000000001000
D:\win95\C>float
1.00001
00111111100000000000000001010100
D:\win95\C>float
1.5
00111111110000000000000000000000
D:\win95\C>float
3.0
01000000010000000000000000000000
D:\win95\C>
|
浮動小数点のもう少し立ち入った説明は、パソコン活用研究PCマニアック道のカセットインターフェースによるデータ通信でも書いています。こちらはBASICの話ですが基本は同じですので参照してみて下さい。
TopPage
|