VB.net Math.Round舍入问题 - 相同输入的不同结果

时间:2017-04-10 09:20:21

标签: vb.net math rounding

我们遇到了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有关的舍入问题的答案,这似乎是无关的。

期待任何回复。

1 个答案:

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

参考MidPointRounding docs