0.1を代入したのに == 0.1 がtrueにならない

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

0.1を代入したのに == 0.1 がtrueにならない

#1

投稿記事 by ふりかけ » 14年前

visual c++ 2008です

double x = 0;
x += 0.1;
cout << x << '\n';
if(x == 0.1){
//処理
}

このようにプログラムしたところ、if文の条件式がtrueになりませんでした。直前のcoutにより画面には0.1が表示されます。
x += 0.1; ではなく x = 0.1; にすれば大丈夫でしたが、納得できないのでお願いします。

beatle
記事: 1281
登録日時: 14年前
住所: 埼玉
連絡を取る:

Re: 0.1を代入したのに == 0.1 がtrueにならない

#2

投稿記事 by beatle » 14年前

0.1を代入したのに == 0.1 がtrueにならないのは、floatやdoubleの仕様ですから諦めるほかありません。
x = 0.1;のときに x == 0.1が真になるのは、おそらくコンパイラの最適化のおかげです。本来は真になりません。
参考http://www.cc.kyoto-su.ac.jp/~yamada/pr ... float.html

beatle
記事: 1281
登録日時: 14年前
住所: 埼玉
連絡を取る:

Re: 0.1を代入したのに == 0.1 がtrueにならない

#3

投稿記事 by beatle » 14年前

ごめんなさい。
いろいろな人と相談した結果、僕が勘違いしていた部分がありましたので訂正します。

規格をあたったわけではないのですが、

コード:

double x = 0.1;
と書いた場合、x == 0.1が真になるかどうかは不明です。処理系依存で、しかもコンパイルオプションによっても違うかもしれません。

一般論として、浮動小数点数を扱う場合に == と != は使いません。これは前回示したリンク先のお話を読めば理由が分かると思います。
そのかわり、不等号 <, <=, >, >= を使って比較します。

コード:

if (0.1 - 0.001 < x && x < 0.1 + 0.001) hoge;
のように。

ふりかけ

Re: 0.1を代入したのに == 0.1 がtrueにならない

#4

投稿記事 by ふりかけ » 14年前

そのサイトは投稿する直前に見ていました、偶然です。
2進数と10進数の小数に誤差ができるのも知っていましたが、それでも妙です。
浮動小数点型に == を使わない常識は知りませんでした。相談できる人がいるのはうらやましいですね。
beatleさんありがとうございました。

一応解決にしますが、厳密なことが分かる方がいたら教えてくれるとすっきりします。

たかぎ
記事: 328
登録日時: 15年前
住所: 大阪
連絡を取る:

Re: 0.1を代入したのに == 0.1 がtrueにならない

#5

投稿記事 by たかぎ » 14年前

beatle さんが書きました:一般論として、浮動小数点数を扱う場合に == と != は使いません。
これはちょっと言い過ぎでは?
どんな目的でどんな処理なのかによります。
分かりやすい具体例としては、除算を行う前に除数がゼロかどうかを判定するには==や!=を使うべきです。

ISLe
記事: 2650
登録日時: 15年前
連絡を取る:

Re: 0.1を代入したのに == 0.1 がtrueにならない

#6

投稿記事 by ISLe » 14年前

Visual C++ 2010 Expressでは再現しないですね。

main関数に質問のコードだけを書いたプログラムでも真になりませんか?

一部のバージョンのDirect3Dではデバイスを初期化すると浮動小数点数演算がfloat精度に固定されるなんてことがあるので、そういったものの影響があったりしないですかね。
例えばDXライブラリ使ってると再現するとか。

(追記)
DXライブラリを使ったプログラムの中に組み込んでみたら再現しました。

beatle
記事: 1281
登録日時: 14年前
住所: 埼玉
連絡を取る:

Re: 0.1を代入したのに == 0.1 がtrueにならない

#7

投稿記事 by beatle » 14年前

たかぎ さんが書きました:
beatle さんが書きました:一般論として、浮動小数点数を扱う場合に == と != は使いません。
これはちょっと言い過ぎでは?
どんな目的でどんな処理なのかによります。
分かりやすい具体例としては、除算を行う前に除数がゼロかどうかを判定するには==や!=を使うべきです。
確かに言い過ぎだったかもしれません。その上で少し意見を書きます。
浮動小数点数同士の除算の場合、ゼロ除算はエラーになりません。少なくともIEEE 754の浮動小数点なら。
IEEE 754の浮動小数点でゼロ除算をすると「無限大」になります。
しかも、ゼロでない数値で割った時も「無限大」になることはあります。例えばこういう場合。

コード:

int main(void)
{
    float a = 3e38f;
    printf("a = %f\n", a);

    float b = a / 1e-1f;
    float c = a / 0.0f;
    printf("b = %f\n", b);
    printf("c = %f\n", c);
    return 0;
}
b, cいずれも"inf"と表示されます。
従って、浮動小数点数の除算において除数が「0.0」かどうかチェックするのはあまり意味がないと思います。
ですから、僕としては「分かりやすい具体例として」、「除算を行う前に除数がゼロかどうかを判定する」という例は不適当だと思います。
除算する前にゼロかどうかチェックしないといけない、チェックすることに意味がある、と誤解を与えてしまいそうです。

興味でお聞きしたいのですが、他に、==や!=を使うべきである例はありますか?
便乗質問になっていますがお許し下さい。

たかぎ
記事: 328
登録日時: 15年前
住所: 大阪
連絡を取る:

Re: 0.1を代入したのに == 0.1 がtrueにならない

#8

投稿記事 by たかぎ » 14年前

beatle さんが書きました:従って、浮動小数点数の除算において除数が「0.0」かどうかチェックするのはあまり意味がないと思います。
ですから、僕としては「分かりやすい具体例として」、「除算を行う前に除数がゼロかどうかを判定する」という例は不適当だと思います。
IEEE754以外の場合もあるというのはおいておいて...
無限大という結果を得てそれで済む場合ならそれでもよいでしょうが、そうでない場合もあります。
例えば、点(x0, y0)と点(x1, y1)を結ぶ直線の方程式を求める場合を考えてみてください。
このとき、x0 != x1であれば直線の傾きは(y1 - y0) / (x1 - x0)になりますが、x0 == x1 または (x1 - x0) == 0 の場合の方程式は x = x0 ですので、通常、処理を変えないといけなくなります。
beatle さんが書きました:興味でお聞きしたいのですが、他に、==や!=を使うべきである例はありますか?
便乗質問になっていますがお許し下さい。
特殊な状況ではいろいろあるのですが、状況説明が大変です。
誰でもわかる例としては、二次方程式が重解を持つかどうかを調べるには、判別式がゼロかどうかで判断するしかありません。
誤差を考慮して、判定式がゼロの近傍であれば重解とする方針もあり得ますが、常にそうすべきとは限りません。
あとは、HUGE_VALと比較する場合には==や!=を使うことは普通にありますね。

たかぎ
記事: 328
登録日時: 15年前
住所: 大阪
連絡を取る:

Re: 0.1を代入したのに == 0.1 がtrueにならない

#9

投稿記事 by たかぎ » 14年前

たかぎ さんが書きました:誰でもわかる例としては、二次方程式が重解を持つかどうかを調べるには、判別式がゼロかどうかで判断するしかありません。
書いた後に気付きましたが...
二次方程式 ax2+bx+c=0 のa, b, cを与えて解を求める場合、解の公式をそのまま使ってしまうと、a=0のときに|x|=∞という結果になってしまいますね。
これは判別式以前の問題です。
数学的には、ゼロで割った結果はそもそも無限大ではないので、何でもかんでもそれで代用するのは無理があるのです。

beatle
記事: 1281
登録日時: 14年前
住所: 埼玉
連絡を取る:

Re: 0.1を代入したのに == 0.1 がtrueにならない

#10

投稿記事 by beatle » 14年前

たかぎさん、分かりやすい説明ありがとうございます。
確かに==や!=で比較するのが必要な場合があることが分かりました。

box
記事: 2002
登録日時: 15年前

Re: 0.1を代入したのに == 0.1 がtrueにならない

#11

投稿記事 by box » 14年前

たかぎ さんが書きました: 二次方程式 ax2+bx+c=0 のa, b, cを与えて解を求める場合、解の公式をそのまま使ってしまうと、a=0のときに|x|=∞という結果になってしまいますね。
といいますか、aが「正確に」0の場合はそもそも二次方程式にならないので、
aが「正確に」0かどうかを判定した後で解の公式を適用するのが筋かと。

たかが二次方程式、されど二次方程式。曰く侮り難し。
バグのないプログラムはない。
プログラムは思ったとおりには動かない。書いたとおりに動く。

閉鎖

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