if文で判定結果が真なのにコードが実行されない

[戻る]
ラジアン 2003/09/18(木) 20:25:37
現在、特定の点が線分上にあるか判定する関数を作っています。
それに以下のコードを使用しています。
しかし、最初のif文ではメッセージボックスが表示されません。
どうしてなのでしょうか?
開発環境  Win2000 VC++6.0 Pro APIのみ

このとき、Xは確実に0ではなく、cは線分の中点です。
当然ですが、ansと最初のif文での演算結果は同じです

float  X    = pVertex[1].x - pVertex[0].x,
       A    = (pVertex[1].y - pVertex[0].y) / X,
       ans = ( (A * c.x) + ( pVertex[0].y - ( A * pVertex[0].x ) ) );

//メッセージボックスが表示されない
if(c.y ==  ( (A * c.x) + ( pVertex[0].y - ( A * pVertex[0].x ) ) ) )
    MessageBox(NULL,"線分上","",MB_OK);

//メッセージボックスは表示される
if(c.y ==  ans)
    MessageBox(NULL,"線分上","",MB_OK);

Tonny 2003/09/18(木) 21:48:09
思いっきりはずしていたらすみません。型がdoubleだったりしていませんか。

ラジアン 2003/09/18(木) 21:55:56
floatのはずです。
線分の始点と終点はDirectXのD3DXVECTOR2構造体なので

アイススケーター 2003/09/18(木) 22:03:05
デバッガーでステップ実行して、ウォッチで確認してみてください。

Tonny 2003/09/18(木) 22:03:09
if(c.y == (float) ( (A * c.x) + ( pVertex[0].y - ( A * pVertex[0].x ) ) ) )
    MessageBox(NULL,"線分上","",MB_OK);
ではいかがですか。

ラジアン 2003/09/18(木) 22:09:02
Tonnyさんの通りにしてみましたが駄目でした。
アイススケーターさん、何を確認するんでしょうか?

アイススケーター 2003/09/18(木) 22:16:40
>アイススケーターさん、何を確認するんでしょうか?
こんな質問が返ってくるとは思いませんでしたが

if文の右辺と左辺が同じかどうかです。

右辺は1個の変数に置き換えて確認したほうがデバッグしやすいですよ。

多分答えだと思いますが、誤差を考慮していないからではないかと思います。

例えば、実数で3と8の中心は5.5ですが整数で計算すると5になってしまう。
floatでも当然、誤差は発生します。

まずはデータを確認されては

Tonny 2003/09/18(木) 22:21:49
>Tonnyさんの通りにしてみましたが駄目でした。
大変失礼しました。
勿論c.yやansがintなんて事もないですよね。
お力になれないのにお手間取らせてしまい申し訳ありませんでした。

ラジアン 2003/09/18(木) 22:29:50
しかし、デバッガで値を調べても等しいです。
c.y ==  ( (A * c.x) + ( pVertex[0].y - ( A * pVertex[0].x ) ) ) 
をデバッガで調べると1になっています。
c.y == ans
も同様です。

アイススケーター 2003/09/18(木) 22:44:50
>MessageBox(NULL,"線分上","",MB_OK);
これは確実に表示するのを確認しました。
そうすると、どうしてもif文が成立していないとしかおもえないのですが。

ステップでMessageBox(NULL,"線分上","",MB_OK);まで行ってますか。

ラジアン 2003/09/18(木) 22:49:29
ちゃんとやっているんですけど
最初のif文はMessageBox(NULL,"線分上","",MB_OK);を飛びこえてしまいます。
次のif文ではちゃんと表示されるんですけどね

アイススケーター 2003/09/18(木) 22:59:13
飛び越すということは、左辺と右辺が違うということでしょう。
不思議ですね

ソースがあれば解析してみますが、必要な部分だけ提示されてはいかが

但し、今日は帰ります おやすみなさい。

ラジアン 2003/09/18(木) 23:08:11
最初とあんまり変わりませんが一応ソースを載せておきます。
pVertexはWM_LBUTTONDOWNで得たマウス座標そのままです。

c = pVertex[0] + pVertex[1];
c /= 2.0f;

//ここでXは確実に0ではない
float X   = pVertex[1].x - pVertex[0].x,
      A   = (pVertex[1].y - pVertex[0].y) / X,
      ans = ( (A * c.x) + ( pVertex[0].y - ( A * pVertex[0].x ) ) );

//メッセージボックスが表示されないが、デバッガでの判定は1になっている
if(c.y ==  ( (A * c.x) + ( pVertex[0].y - ( A * pVertex[0].x ) ) ) )
    MessageBox(NULL,"交差しました","",MB_OK);

//メッセージボックスが表示される
if(c.y == ans)
    MessageBox(NULL,"交差しました","",MB_OK);

tib 2003/09/18(木) 23:14:48
==の代わりに差の絶対値がDBL_EPSILONかDBL_MINより小さいかどうか、という判定方法に
するとか。

ラジアン 2003/09/18(木) 23:28:51
if(DBL_EPSILON < 
            (c.y - ( (A * c.x) + ( pVertex[0].y - ( A * pVertex[0].x ) ) ) ) )
    MessageBox(NULL,"交差しました","",MB_OK);
ということでしょうか?
確かにこれならうまくいきました。
これってやっぱり誤差が原因ってことでしょうか

Tonny 2003/09/18(木) 23:54:56
お口汚しな意見ですみません。ラジアンさんを怒らせちゃったのは私のような...
本当にごめんなさい。

float(4byte)と別な値(例えばint(4byte)(定数もintとみなされる))を計算させると、
まずどっちかかの型に直さないといけないわけで、でもfloatとint、どっちにキャスト
しても値が保証されないので、そういう場合に一時的にDoubleに
変換して計算しておいて、計算したあとで、左辺に代入する時にその型にキャストする、
みたいな事を読んだ記憶があったんです。

だから代入を行っている
ans = ( (A * c.x) + ( pVertex[0].y - ( A * pVertex[0].x ) ) );
ではちゃんとfloatになっているのに、比較を行っている
if(c.y ==  ( (A * c.x) + ( pVertex[0].y - ( A * pVertex[0].x ) ) ) )
はdoubleのままなので、答えが違っちゃうのかな、と...

それでできなかった理由は、私も知りたいです。
コンパイラのバグということならそれでも納得できます。
でもとりあえずできたから、いいや。
ではなく、原因をぜひとも知りたいです。

Tonny 2003/09/19(金) 00:02:22
PS.私の質問も誰か答えてえ...。(T.T)

ラジアン 2003/09/19(金) 00:27:02
[[解決]]
doubleで計算してみたら小数点以下7桁以降になんか数値が出てきていました。
どうやらTonnyさんの言う通りのようです。
解決です。

2003/09/19(金) 00:59:43
>だから代入を行っている
>ans = ( (A * c.x) + ( pVertex[0].y - ( A * pVertex[0].x ) ) );
>ではちゃんとfloatになっているのに、比較を行っている
>if(c.y ==  ( (A * c.x) + ( pVertex[0].y - ( A * pVertex[0].x ) ) ) )
>はdoubleのままなので、答えが違っちゃうのかな、と...

なかなかいいところをついているとは思いますが、コンパイラーがどのようなコードを
生成しているかは、実際のコードを見てみなければなりません
----コンパイラーオプションによっても変ってくるからです----
しかし、486 以降の x86 では浮動小数点数の計算はコプロセッサーが担当し、
コプロセッサー内部の精度は 80bit ですから double のままという点には多少の
問題が残ると思います(80bit の方が double よりは少しだけ精度が高いから)

if (c.y == ((A * c.x) +(pVertex[0] - y - (A * pVertex[0].x ))); では
恐らく全てコプロセッサー内での計算をするようなオブジェクトコードを生成する
でしょう(等しいかどうかの判定まで)。そうした場合、float よりははるかに長い
仮数の桁数で計算しますから比較する対象の右の式の方は掛け算や加算や減算やが
入っているので左の c.y の float から 80bit に変換した内部形式ではおそらく
有効な桁数は元の float のままでしょうから、それよりも仮数の有効な値の桁数は
長くも短くもなり得るので、一致しないと判定してもおかしくは無いでしょう

一方、float の変数 ans に代入する際は、仮数の桁数もコプロセッサーの内部形式
から float に変換がおこるので僅(わず)かな違いが見えなくなってしまうのかも
しれません

特定の数値の組み合わせでは、この場合の計算でたまたま一致することがあるのでしょうが
浮動小数点数の場合、等しいかどうかの判定は比較したい数同士が十分近いかどうかで
判定するのが望ましいと私も思います(どの程度なら十分近いかという事が一言で言え
ないので私には尋ねないで下さい)

古いスレッドには追加発言できません。