tagCANDY CGI VBレスキュー(花ちゃん) の Visual Basic 2010 用 掲示板(VB.NET 掲示板) [ツリー表示へ]   [Home]
一括表示(VB.NET VB2005)
タイトルIF文 複数条件判定のバグ
記事No9226
投稿日: 2009/07/30(Thu) 12:42
投稿者おーたか
VB.2005でBASICプログラムをVBに変換する作業をしています。
OSはXPです。
VB暦は5年ほどですが、技術力はまだまだ・・の者です。

以下のような、IF文の複数条件での判定文を作りたいのですが、
思ったところに入っていきません。

(実際はa,b,c,dはpublic変数です)
    dim a as single = 0.0194
    dim b as single = -0.56
    dim c as single = 2.18
    dim d as integer = 20
    dim kekka as string

    If a = 0.0957 AndAlso b = 2.84 AndAlso c = -0.8 AndAlso d = 40 Then
            kekka = "あ"
    ElseIf a = 0.0194 AndAlso b = -0.56 AndAlso c = 2.18 AndAlso d = 20 Then
            kekka = "い"
    ElseIf a = 0.0027 AndAlso b = 8.51 AndAlso c = 1.2 AndAlso d = 20 Then
            kekka = "う"
    ElseIf a = 0.0095 AndAlso b = 0.37 AndAlso c = 1.4 AndAlso d = 40 Then
            kekka = "え"
    ElseIf a = -0.0041 AndAlso b = -1.92 AndAlso c = 2.34 AndAlso d = 20 Then
            kekka = "お"
                      ・・・
           ・・・
           ・・・
      (ELSEIF文は30くらいあります)
    Else
      kekka = "ERROR!!!"
    EndIf

以上のプログラムだと
    kekka = "い"
となるはずなのですが、実際はELSE文に入ってしまいます。

おそらく非常に単純なバグがあると思うのですが、
私一人では見つけられないでおります。

どなたか、ご教授願います。

[ツリー表示へ]
タイトルRe: IF文 複数条件判定のバグ
記事No9227
投稿日: 2009/07/30(Thu) 13:28
投稿者るしぇ
>私一人では見つけられないでおります。
最終的にそれなら仕方ないけど、まだまだできることはあるんじゃないの?
少なくとも条件を1つ1つ検証するくらいはプログラムを知らない人でも
考えられるのでは?
        Dim a As Single = 0.0194
        Dim b As Single = -0.56
        Dim c As Single = 2.18
        Dim d As Integer = 20
        Dim o As Object
        Debug.WriteLine(a)
        Debug.WriteLine(b)
        Debug.WriteLine(c)
        Debug.WriteLine(d)
        If Not (a = 0.0194) Then
            Debug.WriteLine("Not a")
            o = 0.0194
            Debug.WriteLine(o.GetType)
            Debug.WriteLine(o)
            Debug.WriteLine(0.0194 - a)
        End If
        If Not (b = -0.56) Then
            Debug.WriteLine("Not b")
            o = -0.56
            Debug.WriteLine(o.GetType)
            Debug.WriteLine(o)
            Debug.WriteLine(-0.56 - b)
        End If
        If Not (c = 2.18) Then
            Debug.WriteLine("Not c")
            o = 2.18
            Debug.WriteLine(o.GetType)
            Debug.WriteLine(o)
            Debug.WriteLine(2.18 - c)
        End If
        If Not (d = 20) Then
            Debug.WriteLine("Not d")
            o = 20
            Debug.WriteLine(o.GetType)
            Debug.WriteLine(o)
            Debug.WriteLine(20 - d)
        End If

> 0.0194
> -0.56
> 2.18
> 20
> Not a
> System.Double
> 0.0194
> -5.72204589266434E-10
> Not b
> System.Double
> -0.56
> 2.38418573772492E-09
> Not c
> System.Double
> 2.18
> -6.67572019885654E-08

はっきりとした理由までは示せないけど、とりあえず Double で処理されてます。
型変換せずに出力するとおかしな点は出力できませんが、演算すると丸め誤差を
出力できるようです。
IF 判定時に同様の型変換が起こってるかどうかなどの詳細説明までになってくると
もう少し詳しい人の意見を待ったほうが良いでしょう。
上の検証結果では完全に説明はできていませんのでお間違いなく。

[ツリー表示へ]
タイトルRe^2: IF文 複数条件判定のバグ
記事No9234
投稿日: 2009/07/31(Fri) 10:32
投稿者おーたか
るしぇ様
早速のご回答ありがとうございます。
しかも検証もしていただき、ありがとうございます。助かります。

> はっきりとした理由までは示せないけど、とりあえず Double で処理されてます。
> 型変換せずに出力するとおかしな点は出力できませんが、演算すると丸め誤差を
> 出力できるようです。
> IF 判定時に同様の型変換が起こってるかどうかなどの詳細説明までになってくると
> もう少し詳しい人の意見を待ったほうが良いでしょう。


るしぇ様の検証より、内部的にはDoubleで処理されているのかと思い、
以下のようにDoubleで型変換して、DOUBLEで判定してみました。

(実際はa,b,c,dはpublic変数です)
    dim a as double = CDbl(0.0194)
    dim b as double = CDbl(-0.56)
    dim c as double = CDbl(2.18)
    dim d as integer = 20
    dim kekka as string

    If a = 0.0957 AndAlso b = 2.84 AndAlso c = -0.8 AndAlso d = 40 Then
            kekka = "あ"
    ElseIf a = 0.0194 AndAlso b = -0.56 AndAlso c = 2.18 AndAlso d = 20 Then
            kekka = "い"
    ElseIf a = 0.0027 AndAlso b = 8.51 AndAlso c = 1.2 AndAlso d = 20 Then
            kekka = "う"
                      ・・・
           ・・・
           ・・・
      (ELSEIF文は30くらいあります)
    Else
      kekka = "ERROR!!!"
    EndIf

しかし、DOUBLEにすると誤差があり、
やはり条件判定文には引っかかってくれませんでした。

結局のところ、浮動小数点での条件判定は難しい、ということでしょうか?
それとも
    IF a = 0.0957 then
ではなく、
  IF a >=0.0957 AndAlso a < 0.0958 then
と誤差分を考慮して書かなくてはならないのでしょうか?

なにぶん判定条件が多いので、
もう少しシンプルな書き方があったらうれしいのですが・・・

[ツリー表示へ]
タイトルRe^3: IF文 複数条件判定のバグ
記事No9236
投稿日: 2009/07/31(Fri) 11:07
投稿者るしぇ
> しかし、DOUBLEにすると誤差があり、
> やはり条件判定文には引っかかってくれませんでした。
こっちの環境では引っ掛かったよ。
デバッグ出力して具体的な検証結果を示してください。

[比較演算子 (Visual Basic)]
http://msdn.microsoft.com/ja-jp/library/cey92b0t(VS.80).aspx
数値の比較として型変換されることは書いてありました。

[ツリー表示へ]
タイトルRe^4: IF文 複数条件判定のバグ
記事No9238
投稿日: 2009/07/31(Fri) 13:50
投稿者おーたか
るしぇ様
再回答ありがとうございます。

> こっちの環境では引っ掛かったよ。
> デバッグ出力して具体的な検証結果を示してください。

こちらでも質問で出した例題では引っかかりましたが、
実際のプログラムでは引っかかりませんでした。

本当のコードは、次のように、
Public変数をBBB()でセットし、AAA()で比較します。
まさかローカル変数の場合とPublic変数の場合で、
このような違いが生まれるとは思わず、
例題として単純にするように、ローカル変数として1関数内にまとめてしまいました。
お手数をおかけして申し訳ありませんでした。


引っかからなかった実際の例)
public alpha as single
public beta as single
public gannma as single
public r as inetger

private sub BBB()
    alpha = 0.0035
    beta = -2.33
    gannma = 2.72
    r = 40
end sub

private sub AAA()
        Dim a As Double = CDbl(alpha)
        Dim b As Double = CDbl(beta)
        Dim c As Double = CDbl(gannma)
        Dim d As Integer = r
        Dim o As Object
        Debug.WriteLine(a)
        Debug.WriteLine(b)
        Debug.WriteLine(c)
        Debug.WriteLine(d)
        If Not (a = 0.0035) Then
            Debug.WriteLine("Not a")
            o = 0.0035
            Debug.WriteLine(o.GetType)
            Debug.WriteLine(o)
            Debug.WriteLine(0.0035 - a)
        End If
        If Not (b = -2.33) Then
            Debug.WriteLine("Not b")
            o = -2.33
            Debug.WriteLine(o.GetType)
            Debug.WriteLine(o)
            Debug.WriteLine(-2.33 - b)
        End If
        If Not (c = 2.72) Then
            Debug.WriteLine("Not c")
            o = 2.72
            Debug.WriteLine(o.GetType)
            Debug.WriteLine(o)
            Debug.WriteLine(2.72 - c)
        End If
        If Not (d = 40) Then
            Debug.WriteLine("Not d")
            o = 40
            Debug.WriteLine(o.GetType)
            Debug.WriteLine(o)
            Debug.WriteLine(40 - d)
        End If

        If a = 0.0957 AndAlso b = 2.84 AndAlso c = -0.8 AndAlso d = 40 Then
            kuiki = "(あ)"
        ElseIf a = 0.0194 AndAlso b = -0.56 AndAlso c = 2.18 AndAlso d = 20 Then
            kuiki = "(い)"
        ElseIf a = 0.0027 AndAlso b = 8.51 AndAlso c = 1.2 AndAlso d = 20 Then
            kuiki = "(う)"
        ElseIf a = 0.0095 AndAlso b = 0.37 AndAlso c = 1.4 AndAlso d = 40 Then
            kuiki = "(え)"
         ・・・
         ・・・
        ElseIf a = 0.0035 AndAlso b = -2.33 AndAlso c = 2.72 AndAlso d = 40 Then
            kuiki = "(こ)"
         ・・・
        Else
            kuiki = "ERROR!!!"
        End If
End Sub

以上の場合だと、ELSE文に行ってしまいます。

-----------------Doubleでキャストした場合の結果
0.00350000010803342
-2.32999992370605
2.72000002861023
40
Not a
System.Double
0.0035
-1.08033418582537E-10
Not b
System.Double
-2.33
-7.62939453835543E-08
Not c
System.Double
2.72
-2.86102292967882E-08
-------------------
-------------------a,b,c,dをSingleのまま使った場合の結果
0.0035
-2.33
2.72
40
Not a
System.Double
0.0035
-1.08033418582537E-10
Not b
System.Double
-2.33
-7.62939453835543E-08
Not c
System.Double
2.72
-2.86102292967882E-08
-------------------


> [比較演算子 (Visual Basic)]
> http://msdn.microsoft.com/ja-jp/library/cey92b0t(VS.80).aspx
> 数値の比較として型変換されることは書いてありました。

上記ホームページ見てみました。
別な型同士の比較は、型変換すると書いてありましたが、
今回の場合はどちらもSingleです。
それでもpublic変数ではdoubleとして扱われるのでしょうか?

例として質問に出したソースコードが正確なものでなかったことをお詫びします。
それでも許していただけたら、ご教授願います。

[ツリー表示へ]
タイトルRe^3: IF文 複数条件判定のバグ
記事No9237
投稿日: 2009/07/31(Fri) 13:12
投稿者よねKEN
>     IF a = 0.0957 then
> ではなく、
>   IF a >=0.0957 AndAlso a < 0.0958 then
> と誤差分を考慮して書かなくてはならないのでしょうか?

そうですね。一般論としては浮動小数点の比較では、

If Math.Abs(a - 0.0957) < 0.0001 then

のように誤差の範囲内かどうかで比較する必要があります。
提示されているコードには書かれていませんが、変数a〜dの値は
If文の前に計算か何かして値を取得していると思いますので、
浮動小数点数を使う限りは、どうやっても誤差の入り込む余地はあります。

SingleやDoubleの代わりにDecimalを使うという手もありますけどね。

[ツリー表示へ]
タイトルRe^4: IF文 複数条件判定のバグ
記事No9239
投稿日: 2009/07/31(Fri) 14:04
投稿者おーたか
よねKEN様、ご回答ありがとうございます。

色々考えまして、Single、Doubleとして扱うよりも、
文字列として扱って判定した方が、今後の修正が楽な気がしてきました。
なのでStringとして修正しようと思います。

今回の件で、
浮動小数点の条件判定が、想像以上に面倒だとわかりました。
勉強になりました。

るしぇ様、よねKEN様、ありがとうございました。

[ツリー表示へ]