Version 12.04
浮動小数点型
「今回は、実数を入れる型について説明します」
『前回 double とか使ったよね』
「そう、 double と、あと float っていうのに実数を入れられます」
void Use_double()
{
double d = 0.1;
TRACE( "%.20f\n", d );
// 0.10000000000000001000
}
「 float の例はパス。 float は double の半分のサイズだから、普段は使
わなくていいと思うよ」
『半分のサイズって、具体的には?』
「 float が4バイト、 double が8バイト」
『8……大きいね。 int が4バイトだからその倍だ』
「ま、元々 float があって、その倍だから」
『ダブル?』
「たぶん」
『たぶんって……』
「で、じゃあ float ってどういう意味かっていうと、 float も double も
併せて、実数を入れるための型を【浮動小数点型】って言います」
『ふどうしょうすうてんがた……』
「この【浮】が float ってこと」
『で、その浮動小数点型ってどういう意味?』
「その名の通り、小数点が動くって意味」
『小数点が動く? それって 0.1 が 0.01 になっちゃうとかそういうこ
と?』
「そういうわけじゃなくて……この説明がまず難しいかも」
『う”』
「えっと、まず、これから説明するのは簡単な概念で、正確な内容じゃない
から。正確なのは次回説明するからね」
『うん、なんとなくわかればいい、ってわけね』
「そういうこと。で、その浮動小数点の概念だけど、前回の E を使った表
現を思い出して」
『普通の数字に、10の何乗か、っていうのが E の後ろに付いた形だよ
ね』
「そう、たとえば次のような感じ」
0.1 = 1.0 E -1
1 = 1.0 E 0
10 = 1.0 E 1
『 E の 0 ?』
「 10 の 0 乗は 1 だから」
『あ、それに 1.0 をかけて 1 なわけね』
「この表現はちょっと難しいかもしれないからもう少し例をあげると」
0.125 = 1.25 E -1
500 = 5.00 E 2
30.03 = 3.003 E 1
「っていう感じに、一番左側の桁が一桁目に来るようにするのがポイント」
『つまりそうなるように小数点をずらして……ずらして? もしかして』
「そう、それが【浮動小数点】って意味。浮動小数点型は、小数点をずらし
て一番左の数字が一桁目に来るようにして、そのずらした分を乗数として別
に書く、ってする方法を取ってるんです」
『なるほどねー』
「で、ここで用語を憶えてもらいたいんだけど、普通の数字の部分を
【仮数部】、 E の右側の乗数を【指数部】っていいます」
『かすうぶとしすうぶ、ね』
「さっきの例だとこうなります」
仮数部
▽
0.125 = 1.25 E -1
500 = 5.00 E 2
30.03 = 3.003 E 1
△
指数部
「 float も double も、こういう形式で値を格納してるんです。仮数部と
指数部、それと符号を別々に」
『符号?』
「プラスかマイナスか」
『あー』
「この形式が使われる理由は、前回も言ったけど、元々は科学的な計算に使
うため」
『誤差がどうとかってゆーのね』
「そう。たとえば、縦が2メートル、横が3メートルの長方形の面積は?」
『6平方メートル』
「じゃあ、縦が2キロメートルで横が3キロメートルの長方形の面積は?」
『6平方キロメートル』
「このふたつの違いは単位。もしキロメートルに単位を合わせると、最初の
例は 0.002 キロメートルX 0.003 キロメートル = 0.000006 平方キロ
メートルになります」
『なんかやったら小さそうだね』
「小さいから四捨五入したりすると 0 になります」
『げ!』
「っていうのはまずいでしょ」
『うん、まずい』
「単位が変わることで計算結果が変わっちゃだめ、ここで重要なのは
2x3=6っていう計算。そのために重要なのが、指数部の存在」
『指数部……あ、そっか、10を掛けるっていうのが単位が変わるってこと
になるんだ』
「そういうこと。科学的な計算で一番重要なのは仮数部の計算結果。指数部
に関しては単位を表してるだけ」
『だから分けてるわけねー』
「この仮数部と指数部、あと符号が、 double だったら8ビットの中に特殊
な形で格納されてます。それを見るのは次回」
『……まだ半分残ってるよ?』
「というわけで、次回使う関数を先に用意します。ビットはメモリウィンド
ウとかでは見られないから、 double をビットで出力する関数を使います」
// double の中身をビットで表示します。
void OutputDoubleBit( double p_d )
{
// double のサイズ。
const int iDoubleSize = sizeof( p_d );
// バイトごとのループ。
// 後ろから見ていくのはバイトオーダーの関係です。
for( int iF1 = iDoubleSize - 1
; iF1 >= 0
; --iF1
)
{
CString cStr;
unsigned char uch
= ( (unsigned char *)( &p_d ) )[iF1];
// ビットごとのループ。
for( int iF2 = 0; iF2 < 8; ++iF2 )
{
CString cOneBitStr;
cOneBitStr.Format
( "%d"
, ( uch >> ( 1 * iF2 ) ) & 0x01
);
cStr = cOneBitStrpch + cStr;
}
TRACE( "%s ", cStr );
}
TRACE( "%.20f\n", p_d );
}
『中身はそれほどじゃないけど、結構長いね』
「2重ループになってるのは、外はバイトごと、中はビットごとに処理する
ようになってるから。つまり、ひとつバイトを取ってきて、その中を各ビッ
トごとに出力して、全部出力したら次のバイト、ってしてます」
『バイトオーダーって、 Version 4.07 ( No.057 ) でやった、逆順に並ん
でるってあれ?』
「そう、あれ。 int だけじゃなく float や double も逆順に並んでます。
だから、外側のループは右から見ていきます」
『中は普通に左から?』
「中も右から。でも出力はそのままだから」
『どゆこと?』
00000111(2進数)
「ならそのまま」
00000111
「って出るってこと。ビットごとは、バイトオーダーとか関係ないから難し
く考えなくていいよ」
『はーい』
「あと、ちょっと難しいと思う部分」
, ( uch >> ( 1 * iF2 ) ) & 0x01
「の部分をちゃんと見ておこうか」
『確かにこれは難しそう……』
「まずは >> から」
『 Version 4.05 ( No.055 ) のだね。そか、これ使うとビットひとつずら
せるんだ』
「だから、さっきのだと」
00000111 >> 0 : 00000111
00000111 >> 1 : 0000011
00000111 >> 2 : 000001
00000111 >> 3 : 00000
00000111 >> 4 : 0000
00000111 >> 5 : 000
00000111 >> 6 : 00
00000111 >> 7 : 0
「ってなります。これに 0x01 、つまり」
00000001
「を & にかけると…… & は Version 4.05 ( No.055 ) を参照」
『ビットひとつひとつ見て、同じだったら 1 なんだよね』
「そう、だから
0000011 &
00000001 =
00000001
「とか」
0000
00000001
「ってなります。つまり」
, ( uch >> ( 1 * iF2 ) ) & 0x01
『は、一番右のビットが 1 だったら 1 、 0 だったら 0 ってしてるわけ
ね』
「そういうこと。というわけで仕組みがわかったところで、次回に続く!」
/*
Preview Next Story!
*/
「次回はいよいよ実数の中身について見てみます」
『なんかむつかしそう』
「来週はその中でも簡単な方」
『ホントに簡単?』
「……比較的」
『比較的ぃ?』
「というわけで次回」
< Version 12.05 double の中身・指数部編 >
『につづく!』
「ほら、解ってる人は簡単かどうかの比較ができないって言うから」
『ダメじゃん』