パソコン活用研究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