2017-04-10 4 views
1

Wir stoßen auf ein Problem mit VB.NET Math.Round Methode, wo wir verschiedene Ergebnisse für die gleiche Eingabe erhalten.VB.net Math.Round Rundungsproblem - verschiedene Ergebnisse für die gleiche Eingabe

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 

Ausführen dieses Codes gibt die folgende Ausgabe (Kultur nl-NL):

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

mit diesem kurzen Stück Code, um einen einfachen gleitenden Durchschnitt Funktion enthält Das Problem ist reproduzierbar und am besten dargestellt Wir wundern uns, warum die Ausgabe für 2 scheinbar identische Anrufe zu Math.Round unterschiedlich ist. Es sollte keine Genauigkeitsprobleme geben, da der verwendete Wert weniger Ziffern hat als der 15-16 digits, der in einen Double Datentyp passen sollte.

Suche im Internet gibt hauptsächlich Antworten auf Rundungsprobleme mit MidpointRounding, die nicht verwandt scheint.

Wir freuen uns auf jede Antwort.

+1

Wenn ich testen, die über Ihre Funktion 'ExponentialMovingAverage' eine Leistung von 20,419850000000004 – OSKM

+2

Sie produziert * * keine unterschiedlichen Ergebnisse aus den gleichen Eingang. 'Math.Round()' ist deterministisch. Sie haben unterschiedliche Ergebnisse von Eingaben, die * gleich aussehen *. Die zugrunde liegenden Floats befinden sich in Base 2. Ein Artefakt der Anzeige von Base 2-Nummern in Base 10 ist, dass gelegentlich unterschiedliche Zahlen gleich angezeigt werden. Es scheint, dass VB.net leider ein eingebautes 'frexp()' fehlt, das Sie leicht die zugrundeliegenden Bit-Muster untersuchen lässt, aber etwas wie dieses sollte leicht zu vb übersetzt werden, wenn Sie neugierig sind: http: // stackoverflow. com/q/389993/4996248 –

Antwort

0

Ich vermute, es liegt daran, dass Math.Round standardmäßig ToEven als Rundungsmethode verwendet. Wenn Sie zu diesem wechseln:

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)) 

dann ist das Ergebnis, wie Sie erwarten. Ihr fest codierter Wert ist genau 20.41985, also wird ToEven es in 20.4198 umwandeln. Ich vermute, dass Ihr kalkuliertes Ergebnis sehr viel größer ist als 20.41985 (wie oben erwähnt, bekomme ich auch 20.419850000000004 als Ergebnis) und so wird aufgerundet.

Ref MidPointRounding docs

Verwandte Themen