2017-04-10 28 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 

我们想知道为什么输出与Math.Round的两个看似相同的调用有所不同。不应该有任何精度问题,因为所使用的值的位数少于15-16 digits,它应该适合Double数据类型。

搜索网络主要返回与MidpointRounding相关的舍入问题的答案,这似乎与此无关。

期待任何回复。

+1

当我测试你的功能上面'ExponentialMovingAverage'产生的20.419850000000004 – OSKM

+2

输出*您没有*具有相同的输入不同的结果。 'Math.Round()'是确定性的。你看起来相同的输入有不同的结果。底层浮点数位于底数2中。显示基数为10的基数为2的工件偶尔会显示不同的数字。看起来,VB.net不幸的是缺少一个内置的'frexp()',它可以让你轻松地检查底层的位模式,但是如果你好奇的话,应该很容易转换为vb:http:// stackoverflow。 COM/q /4996248分之389993 –

回答

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),因此被四舍五入。

参考MidPointRounding docs

相关问题