我们遇到了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
运行此代码会提供以下输出(culture nl-NL ):
20,41985000000000000000 --> 20,4199
20,41985000000000000000 --> 20,4198
我们想知道为什么两个看似相同的Math.Round
调用的输出不同。不应存在任何精度问题,因为使用的值的数字少于15-16 digits中应该适合Double
数据类型的数字。
搜索网络主要返回与MidpointRounding
有关的舍入问题的答案,这似乎是无关的。
期待任何回复。
答案 0 :(得分: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)因此被四舍五入。