Micomprocedure Wiki
浮動小数点と誤差
をテンプレートにして作成
開始行:
浮動小数点と誤差について
早速例を見てみよう
#code(cpp){{
if(0.1+0.7==0.8){
printf("0.1+0.7==0.8 is true!");
}else{
printf("0.1+0.7==0.8 is false!?");
}
}}
実行すれば分かるが、これは''偽''となる。(はず。少なくともこれを書いた人の環境ではそうなる)
値を変数に代入しても同様。
0.1等は2進数表記では循環小数となる。
そしてメモリは有限のためそれを有限桁で打ち切っている。(最後を0捨1入で丸めている)
そこで誤差が発生しているのである。(これはC言語に限った問題ではない)
上の例でもわかるように、double型などについて==は使わない方が良い。
誤差にはほかにもいくつかの種類のものがある。
気が向いたときに追記する。
~
~
~
蛇足 見なくていいかもかも
・doubleの2進表現
環境依存かもしれないが次の方法で0.1がdoubleの中でどう表現されているか見てみよう
//この部分はもう少しいいプログラムないかなぁ
#code(cpp){{
union{
unsigned long long int intnum;
double doublenum;
}num;
num.doublenum = 0.1;
for(int i=0;i<64;++i){
printf("%d",num.intnum>>63);
num.intnum<<=1;
}
}}
書いた人の環境では次のように表示される。
0011111110111001100110011001100110011001100110011001100110011010
これを見ても何がなにやらさっぱりであろう。
double型は符号部1ビット、指数部11ビット、仮数部52ビットで表現される。
上の値をそれに応じて区切ると次のようになる
0 01111111011 1001100110011001100110011001100110011001100110011010
左から符号部、指数部、仮数部である。
これが次のように解釈される。(以下a^bによってaのb乗を表す)
(-1)^符号部×2^(指数部-1023)×(1+仮数部)
符号部は0なら+、1なら-である。
指数部から1023を引いているのは指数部が1023足された表現がされているためである。
仮数部は小数点以下の部分を表わしている。(1.仮数部 のようになる)
例えば
0.75=0.11=(-1)^0×2^(-1)×(1+0.5)
なので符号部は0、指数部は1022(=-1+1023=01111111110)、仮数部は0.5(=1000000000000000000000000000000000000000000000000000)
よって0.75をdoubleで表すと
0011111111101000000000000000000000000000000000000000000000000000
となる。
では、0.1はどうだろう?
0.1を2進数で表そうとすると、
0.1=0.000110011...
となって0011が無限に続く循環小数になる。
しかし仮数部は52ビットしかない。ここで丸められることになる。
実際doubleでどう表されるか考えよう。
符号部が0なのは良いだろう。
指数部は、仮数部が1+仮数部の形で使われるため、適度に数をずらすことになる。
つまり、0.000110011... => 1.100110011... × 2^(指数部-1023)
となるようにする。今回は2^(-4)でなので指数部は1019(=01111111011)
仮数部は1.100110011...の小数部分なので左から53ビット目まで表示すると
10011001100110011001100110011001100110011001100110011...
左から53ビット目で0捨1入が行われる。左から53ビット目が1であるので、左から53ビット目以降を捨てて、左から52ビット目に1を足すと
1001100110011001100110011001100110011001100110011010
これらをつなげると
0011111110111001100110011001100110011001100110011001100110011010
となる。正にプログラムで表示したものと一致したのである。
また、丸められていることも理解してもらえたと思う。
ちなみに、0.7は
0011111111100110011001100110011001100110011001100110011001100110
0.1+0.7は
0011111111101001100110011001100110011001100110011001100110011001
0.8は
0011111111101001100110011001100110011001100110011001100110011010
わずかにに異なるわけである。
終了行:
浮動小数点と誤差について
早速例を見てみよう
#code(cpp){{
if(0.1+0.7==0.8){
printf("0.1+0.7==0.8 is true!");
}else{
printf("0.1+0.7==0.8 is false!?");
}
}}
実行すれば分かるが、これは''偽''となる。(はず。少なくともこれを書いた人の環境ではそうなる)
値を変数に代入しても同様。
0.1等は2進数表記では循環小数となる。
そしてメモリは有限のためそれを有限桁で打ち切っている。(最後を0捨1入で丸めている)
そこで誤差が発生しているのである。(これはC言語に限った問題ではない)
上の例でもわかるように、double型などについて==は使わない方が良い。
誤差にはほかにもいくつかの種類のものがある。
気が向いたときに追記する。
~
~
~
蛇足 見なくていいかもかも
・doubleの2進表現
環境依存かもしれないが次の方法で0.1がdoubleの中でどう表現されているか見てみよう
//この部分はもう少しいいプログラムないかなぁ
#code(cpp){{
union{
unsigned long long int intnum;
double doublenum;
}num;
num.doublenum = 0.1;
for(int i=0;i<64;++i){
printf("%d",num.intnum>>63);
num.intnum<<=1;
}
}}
書いた人の環境では次のように表示される。
0011111110111001100110011001100110011001100110011001100110011010
これを見ても何がなにやらさっぱりであろう。
double型は符号部1ビット、指数部11ビット、仮数部52ビットで表現される。
上の値をそれに応じて区切ると次のようになる
0 01111111011 1001100110011001100110011001100110011001100110011010
左から符号部、指数部、仮数部である。
これが次のように解釈される。(以下a^bによってaのb乗を表す)
(-1)^符号部×2^(指数部-1023)×(1+仮数部)
符号部は0なら+、1なら-である。
指数部から1023を引いているのは指数部が1023足された表現がされているためである。
仮数部は小数点以下の部分を表わしている。(1.仮数部 のようになる)
例えば
0.75=0.11=(-1)^0×2^(-1)×(1+0.5)
なので符号部は0、指数部は1022(=-1+1023=01111111110)、仮数部は0.5(=1000000000000000000000000000000000000000000000000000)
よって0.75をdoubleで表すと
0011111111101000000000000000000000000000000000000000000000000000
となる。
では、0.1はどうだろう?
0.1を2進数で表そうとすると、
0.1=0.000110011...
となって0011が無限に続く循環小数になる。
しかし仮数部は52ビットしかない。ここで丸められることになる。
実際doubleでどう表されるか考えよう。
符号部が0なのは良いだろう。
指数部は、仮数部が1+仮数部の形で使われるため、適度に数をずらすことになる。
つまり、0.000110011... => 1.100110011... × 2^(指数部-1023)
となるようにする。今回は2^(-4)でなので指数部は1019(=01111111011)
仮数部は1.100110011...の小数部分なので左から53ビット目まで表示すると
10011001100110011001100110011001100110011001100110011...
左から53ビット目で0捨1入が行われる。左から53ビット目が1であるので、左から53ビット目以降を捨てて、左から52ビット目に1を足すと
1001100110011001100110011001100110011001100110011010
これらをつなげると
0011111110111001100110011001100110011001100110011001100110011010
となる。正にプログラムで表示したものと一致したのである。
また、丸められていることも理解してもらえたと思う。
ちなみに、0.7は
0011111111100110011001100110011001100110011001100110011001100110
0.1+0.7は
0011111111101001100110011001100110011001100110011001100110011001
0.8は
0011111111101001100110011001100110011001100110011001100110011010
わずかにに異なるわけである。
ページ名:
サイト内を検索
AND検索
OR検索
操作メニュー
サークル情報
†
名称
東北大学文化部登録団体
東北大学マイコンプロシージャ
部室
川内北キャンパス内
川内サークル部室棟 4階
416号室
設立
1979年4月1日
(44年前)
活動内容
ゲームの制作・発表等
(
詳細
)
連絡先
こちら
↑
Twitter
†
@micom_procedure からのツイート
このウェブページについて
ここを編集
コンテンツ
†
トップページ
マイコンプロシージャとは?
公開中の作品
その他の作品
資料室
情報室
リンク
部内向けページトップ
最新の20件
2023年05月25日
ContactUs
2022年11月03日
公開中の作品/2021年度新入生プロジェクト/Gravity-Lost
公開中の作品/2021年度新入生プロジェクト/はるさがし
2021年12月13日
公開中の作品/2021年度新入生プロジェクト/落単・タイム・アタック!
公開中の作品/2021年度新入生プロジェクト/神社からの脱出
RecentDeleted
公開中の作品/2021年度新入生プロジェクト/THE Unexpected Fighting Operation
公開中の作品/2021年度新入生プロジェクト
2021年12月03日
公開中の作品/2020年度新入生プロジェクト/城熊
公開中の作品/2020年度新入生プロジェクト
2021年07月18日
公開中の作品
ProcGameJam9
Inneer/C++_const
2021年04月18日
公開中の作品/2020年度新入生プロジェクト/CrossFade
2021年04月17日
2021年度新歓情報
公開中の作品/2020年度新入生プロジェクト/TeamC no game
2021年04月07日
ようこそ
公開中の作品/2020年度新入生プロジェクト/トキノカダイ
2021年04月04日
2020年度新歓情報
2020年09月15日
ProcGameJam10
ここを編集