2017-04-10 11 views
1

VB.NETのMath.Roundメソッドの問題が発生しており、同じ入力に対して異なる結果が得られています。問題は再現可能であり、最高の単純移動平均関数を含むコードのこの短い断片を使用して説明:VB.net Math.Round丸め問題 - 同じ入力に対して異なる結果が発生する

Public Class bug 

Public Shared Function ExponentialMovingAverage(Values() As Double) As Double 
    Dim Weight, TotalWeight As Double 
    ExponentialMovingAverage = 0 
    Weight = 1 
    TotalWeight = 0 
    For Each Value In Values 
    ExponentialMovingAverage += Value*Weight 
    TotalWeight += Weight 
    Weight /= 1-2/(Values.Length+1) 
    Next 
    ExponentialMovingAverage /= TotalWeight 
    Return ExponentialMovingAverage 
End Function 

Public Shared Sub Main 
    Dim v As Double = 20.41985000000000000000 
    Console.WriteLine(_ 
    ExponentialMovingAverage({77.474, 1.4018}).ToString("0.00000000000000000000") & " --> " & _ 
    Math.Round(ExponentialMovingAverage({77.474, 1.4018}), 4) & vbCrLf & _ 
    v.ToString("0.00000000000000000000") & " --> " & _ 
    Math.Round(v, 4)) 
End Sub 

End Class 

このコードを実行すると、次の出力が得られる(培養NL-NL):

20,41985000000000000000 --> 20,4199 
20,41985000000000000000 --> 20,4198 

私たちは、2つの一見同じ電話Math.Roundのために出力が異なっているのだろうと思っています。使用される値が、Doubleデータ型に収まる15-16 digitsよりも小さい桁数を持つため、精度上の問題はありません。

ウェブ検索では、主に無関係と思われる丸め問題の回答がMidpointRoundingと返されます。

返信をお待ちしております。

+1

私はあなたの機能の上に 'ExponentialMovingAverage'が20.419850000000004 – OSKM

+2

の出力を生成し、テストのRef MidPointRounding docsあなた* *同じ入力から異なる結果を持っていません。 'Math.Round()'は決定的です。あなたは同じ*見ている入力とは異なる結果を持っています。基礎となる浮動小数点数は、基数2になっています。基数10の基数2の数字を表示する人工物は、時々異なる数値が同じものを表示することがあります。残念ながら、VB.netには、基本的なビットパターンを簡単に検査できる組み込みの 'frexp()'が不足しているようですが、好きな場合はvbに簡単に変換できるはずです:http:// stackoverflow。 co.jp/q/389993/4996248 –

答えて

0

これは、デフォルトでMath.Roundが丸めの手法としてToEvenを使用しているからだと思います。これに切り替えた場合:

Console.WriteLine(_ 
     ExponentialMovingAverage({77.474, 1.4018}).ToString("0.00000000000000000000") & " --> " & _ 
     Math.Round(ExponentialMovingAverage({77.474, 1.4018}), 4, MidpointRounding.AwayFromZero) & vbCrLf & _ 
     v.ToString("0.00000000000000000000") & " --> " & _ 
     Math.Round(v, 4, MidpointRounding.AwayFromZero)) 

結果は期待通りです。あなたのハードコード値はちょうど20.41985なので、ToEvenはそれを20.4198に変えます。私はあなたの計算結果が20.41985よりも非常に細かく(上述のように私は結果として20.419850000000004を得ている)、したがって切り上げていると考えています。

関連する問題