double型の計算ずれについて

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
double型のずれ

double型の計算ずれについて

#1

投稿記事 by double型のずれ » 3年前

c++,プログラミングを始めたばかりのものです。
プログラム中によくわからないことが起こったので質問させていただきます。
今回ゲームのエフェクトを作っていて if 文による制御をしていた時予期しない動きをしたので。
バグ探しの時,簡略化したコードをあげさせていただきます

コード:


#include "DxLib.h"
double a=0;

int WINAPI WinMain(HINSTANCE,HINSTANCE,LPSTR,int){
    ChangeWindowMode(TRUE), DxLib_Init(); //ウィンドウモード変更
	for(int i=0;i<100;i++){
	a+=0.2;//0.2を100回たす
	}
	if(a==0.2*100){
		printfDx("成功");
	}else{
		printfDx("失敗");
	}
	WaitKey();//結果があるまで待つ	
	DxLib_End();// DXライブラリ終了処理
    return 0;
}  
環境は
win7 64bit
VC++ 2010 Express
DxLIb
となっております。

ここで成功が出るようにしたいのですが
どうすればいいのでしょうか。
しょしんしゃですが、アドバイスよろしくお願いします。

つくばさん
記事: 22
登録日時: 3年前

Re: double型の計算ずれについて

#2

投稿記事 by つくばさん » 3年前

すみません Nameを間違えてしまいましたこちらの名前でお願いします

超初級者
記事: 54
登録日時: 5年前

Re: double型の計算ずれについて

#3

投稿記事 by 超初級者 » 3年前

計算で求めた値と20との差がきわめて
小さければ成功とみなす、では
いかがでしょうか。

アバター
みけCAT
記事: 6235
登録日時: 9年前
住所: 千葉県
連絡を取る:

Re: double型の計算ずれについて

#4

投稿記事 by みけCAT » 3年前

無駄に実数演算をしないようにしましょう。

コード:

#include "DxLib.h"
double a=0;

int WINAPI WinMain(HINSTANCE,HINSTANCE,LPSTR,int){
	ChangeWindowMode(TRUE), DxLib_Init(); //ウィンドウモード変更
	int i; // ループの後でも参照できるように外に出す
	for(i=0;i<100;i++){
		a = (i+1)*0.2; // 誤差がどんどん積もるような足し算の計算を避ける
	}
	if(i==100){ // 無用な実数の比較を避ける
		printfDx("成功");
	}else{
		printfDx("失敗");
	}
	WaitKey();//結果があるまで待つ	
	DxLib_End();// DXライブラリ終了処理
    return 0;
}  
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

アバター
みけCAT
記事: 6235
登録日時: 9年前
住所: 千葉県
連絡を取る:

Re: double型の計算ずれについて

#5

投稿記事 by みけCAT » 3年前

double型のずれ さんが書きました:ここで成功が出るようにしたいのですが
どうすればいいのでしょうか。
「成功が出る」とは、元のコードではどのようなことを表しますか?
これだけの情報だと、「成功を出したいのだから、余計な計算をせずに成功を出せばいい」となってしまいます。

コード:


#include "DxLib.h"
// 無駄なグローバル変数を削除

int WINAPI WinMain(HINSTANCE,HINSTANCE,LPSTR,int){
    ChangeWindowMode(TRUE), DxLib_Init(); //ウィンドウモード変更
	// 無駄なループを削除
	// 無駄な分岐を削除
	printfDx("成功"); // 成功が出る
	WaitKey();//結果があるまで待つ	
	DxLib_End();// DXライブラリ終了処理
    return 0;
}  
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

つくばさん
記事: 22
登録日時: 3年前

Re: double型の計算ずれについて

#6

投稿記事 by つくばさん » 3年前

超初心者さん、みけCATさん返信ありがとうございます。

書き方があいまいですみませんでした。
今回求めているのは
変数 aに0.2を100回足したらぴったり0.2*100になるようにしたいものでした.


double型というのはこんなにずれるものなんですか?
できれば double型に足し算するだけで==比較でできるようにしたいのですが
無理なのでしょうか

つくばさん
記事: 22
登録日時: 3年前

Re: double型の計算ずれについて

#7

投稿記事 by つくばさん » 3年前

超初級者 さんすみません名前を間違えてしまいました

アバター
みけCAT
記事: 6235
登録日時: 9年前
住所: 千葉県
連絡を取る:

Re: double型の計算ずれについて

#8

投稿記事 by みけCAT » 3年前

つくばさん さんが書きました:今回求めているのは
変数 aに0.2を100回足したらぴったり0.2*100になるようにしたいものでした.
aを標準の実数型ではなく、適当に実装した数クラスにするといいでしょう。
つくばさん さんが書きました:double型というのはこんなにずれるものなんですか?
DXライブラリを使っていると、float型の精度で計算されているかもしれません。
オフトピック
定量的な評価のコードが出ていないのに「こんなにずれる」とは…?
つくばさん さんが書きました:できれば double型に足し算するだけで==比較でできるようにしたいのですが
無理なのでしょうか
この程度の数なら、整数で計算すればいいでしょう。

コード:


#include "DxLib.h"
double a=0;

int WINAPI WinMain(HINSTANCE,HINSTANCE,LPSTR,int){
	ChangeWindowMode(TRUE), DxLib_Init(); //ウィンドウモード変更
	double adata = 0;
	for(int i=0;i<100;i++){
		a=(adata+=2)/10.0;//0.2を100回たす
	}
	if(adata==200.0){
		printfDx("成功");
	}else{
		printfDx("失敗");
	}
	WaitKey();//結果があるまで待つ	
	DxLib_End();// DXライブラリ終了処理
	return 0;
}
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

アバター
Ketty
記事: 102
登録日時: 5年前

Re: double型の計算ずれについて

#9

投稿記事 by Ketty » 3年前

DXライブラリでは、doubleの精度を保証するためには、
SetUseFPUPreserveFlag(TRUE)を実行する必要があると、↓に書かれています(^v^)

http://hpcgi2.nifty.com/natupaji/bbs/pa ... st&no=1385

つくばさん
記事: 22
登録日時: 3年前

Re: double型の計算ずれについて

#10

投稿記事 by つくばさん » 3年前

返信遅れてすみません
みけCATさん Kettyさん  返信ありがとうございます
残念ながらSetUseFPUPreserveFlag(TRUE)では解決しなかったため、
今回はdouble 型ではなく int 型を使うことで解決させていただきました。
皆さんありがとうございました。

たいちう
記事: 418
登録日時: 9年前

Re: double型の計算ずれについて

#11

投稿記事 by たいちう » 3年前

解決になっているので、質問者さんに読まれないかもしれませんが、
元々の疑問が解決していないのではないかと思うので一応書きます。


0.2というのは簡単な小数で、
0.2 + 0.2 + ... + 0.2 == (0.2 * 100) == 20 なのも自明です。
しかし、これは私たちが馴染んでいる10進数での話です。

コンピューターでは2進数が使われていますが、
0.2を2進数で表すと、0.00110011001100110011... という循環小数になります。
double型はfloat型の倍の精度を持っていますが、
無限に続く小数を扱えるわけではないので、どこかで切り捨てることになります。
0.2に非常に近い数値を100回足しても、ぴったり20になる保証はありません。

この事情を10進数で例えると、1/3 + 1/3 + 1/3 が判りやすいと思います。
1/3 == 0.333333333... と無限に続くので、どこかで打ち切ります。
イメージは以下のようなものです。(あくまでもイメージなので、実際とは違います)

コード:

float  a = 1.0 / 3;   // 0.333333
double b = 1.0 / 3;   // 0.333333333333
double c = b + b + b; // 0.999999999999
if (c == 1) {
	printf("成功");  // ここには来ない
}
結論としては、超初級者さんの書いたように、誤差を考慮するか、
みけCATさんの書いたように、整数で管理するか、ですね。

閉鎖

“C言語何でも質問掲示板” へ戻る