ホームに戻る
浮動小数のメモリ上の扱いについて
まず2進数のおさらい、
2進数は数字の0と1のみで表現する。
0から1ずつ増える場合、0、1、10、11、100・・・と増える。
この場合10進数の2が2進数の10にあたり、
10進数の3は2進数の11、10進数の4は2進数の100にあたる。
10進数の場合は一桁上げるのに10倍するが、
2進数の場合は一桁上げるのに2倍すればよいことになる。
これは一桁下げるには2で割ればよいという考えによる。
すなわち、10進数の0.5は2進数の0.1であり、
10進数の0.25は2進数の0.01であり、
10進数の0.125は2進数の0.001である。
ここから浮動小数について
まず浮動小数というと32ビットの値で、
先頭の1ビットが正か負かのフラグ(0=正、1=負)。
次の8ビットが2^(E−127)のEの値(符号無しの正の値)。
残り23ビットが1.****の「****」部分となります。
これではわけがわからないので例として、
小数の値である「8.75」を浮動小数に変換してみます。
まず整数部分の「8」は2進数で「1000」。
小数部分は「0.5」が2進数で「0.1」、
「0.25」が2進数で「0.01」であるので、
「0.75」は「0.11」ということになります。
すなわち、「8.75」は2進数で「1000.11」となります。
これを無理やり「1.****」の形に変換します。
すなわち、「1.00011 × 2^3」となります。
ここまできたらあとは簡単で、
正か負か?→正なので先頭の1ビットは「0」。
E−127=3→よってE=「130」。
1.****=1.00011→よって「00011」。
ゆえに「8.75」は、
「0 10000010 00011000000000000000000」
と変換されます。
この浮動小数の規格をIEEE754(1985)というそうです。
浮動小数の足し算や引き算はEの値が大きいほうにあわせて計算されるので、
ここで丸め誤差に加えて情報落ちや桁落ちが発生します。
以下に確認用プログラムを用意しました。
/*
* 浮動小数(32ビット値)を 0 と 1 に変換して表示します
*/
#include
#define BYTE_SIZE 8 /* 1バイトの幅 */
#define RETURN_COLS 8 /* 表示する列数 */
typedef union{
float f;
char c[4];
}floatchar;
/* char型の文字を 0 と 1 で出力 */
void print_byte_string(char c);
/* 画面に出力 */
void (*disp)(float);
void disp_le(float);
void disp_be(float);
int main(void)
{
short a = 1;
if(*(char *)&a == 1){
/* リトルエンディアンのとき */
disp = disp_le;
}
else{
/* ビッグエンディアンのとき */
disp = disp_be;
}
disp(1.5);
disp(1.25);
disp(1.75);
disp(1.875);
return 0;
}
/*
* 画面への出力(リトルエンディアン)
*/
void disp_le(float f)
{
int i;
floatchar fc;
fc.f = f;
printf("%f", f);
putchar(':');
for(i = 3; i >= 0; i--)
{
putchar(' ');
print_byte_string(fc.c[i]);
}
putchar('\n');
return;
}
/*
* 画面への出力(ビッグエンディアン)
*/
void disp_be(float f)
{
int i;
floatchar fc;
fc.f = f;
printf("%f", f);
putchar(':');
for(i = 0; i <= 3; i++)
{
putchar(' ');
print_byte_string(fc.c[i]);
}
putchar('\n');
return;
}
/*
* char型の8ビットを 0 と1で出力
*/
void print_byte_string(char c)
{
int i;
for(i = BYTE_SIZE - 1; i >= 0; i--)
{
if(((c >> i) & 1) == 1) /* ビットが 0 か 1 か? */
putchar('1');
else
putchar('0');
}
}