Prinzipielle Rundungsmethoden

Das Runden umfasst einen numerischen Wert in eine angegebene Genauigkeit zum nächsten Wert mit weniger Genauigkeit zu konvertieren. Die Genauigkeit des Rundens nimmt ab, je weiter der Wert zu seinem sogenannten Mittelpunktwert kommt. Der Mittelpunktwert, wenn man auf 2 Kommastellen rundet, ist zum Beispiel 3,475.

Nun gibt es zwei Methoden, diese Zahl zu runden:

  • ToEven (Banker’s Rounding)
  • AwayFromZero

Bei ToEven wird der Mittelpunktwert zur nächsten geraden Zahl gerundet (Bsp.: 3,465 => 3,46; 3,475 => 3,48), bei AwayFromZero wird der Mittelpunktwert aufgerunden. Bei allen anderen Zahlen runden die beiden Methoden gleich.

Laut IEEE-Standard Abschnitt 754,4 entspricht das Runden nach der ToEven-Methode dem Standard. In der Schule gelehrt wird die Rundungsmethode AwayFromZero. Glaubenskrieg?

Math.Round

Math.Round im .NET-Framework unterstützt beide Rundungsmethoden, die in einem optionalen Parameter angegeben werden können. Standardmäßig wird bei Math.Round (vielleicht ein wenig überraschend) nach der ToEven-Methode gerundet.

string.Format

Um eine gleichmäßige Ausgabe von zum Beispiel Geldwerten zu erzwingen, wird gerne string.Format eingesetzt (z.B. string.Format(„{0:N2}“, price);).

Im Gegensatz zu Math.Round wird bei string.Format hingegen bei allen Zahlenformaten nach der AwayFromZero-Methode gerundet. Das ist leider auch nicht wirklich dokumentiert. Es werden zwar Beispiele angeführt, die es zeigen, aber änderbar ist die Rundungsmethode bei string.Format nicht.

Fazit

Hilft also nur, alle Zahlen vor deren Ausgabe zu runden und beim Runden immer das MidpointRounding anzugeben, das man anwenden möchte.

Meiner Meinung nach ist das jedenfalls eine außerordentlich große Fehlerquelle …

LG,
Sabine.

Nachtrag

Vorsicht geboten ist auch bei der Verwendung des Datentyps double.

Gegeben sei z. B. der folgende double-Wert:
double dd = 0.034999999999999996;

In folgenden C# Anweisungen ergibt das Runden auf 2 Nachkommastellen jeweils den Wert 0,03:
var a = Math.Round(dd, 2); // ==> 0,03
a = Math.Round(dd, 2, MidpointRounding.AwayFromZero); // => 0,03
a = Math.Round(dd, 2, MidpointRounding.ToEven); // => 0,03

Hingegen ergibt das Runden auf 2 Nachkommastellen per string.Format den Wert „0,04“:
var s = string.Format("{0:N2}", dd); // => "0,04"

Wird in obigen Beispiel anstelle eines double Wertes ein decimal Wert verwendet, ergibt das Runden  in allen gezeigten Fällen den erwarteten Wert 0,03.

Grundsätzlich sollten für Zahlen, bei denen eine exakte Rundung unumgänglich ist (Geldbeträge, Steuern, etc.) der Datentyp decimal verwendet werden.
In Fällen, bei denen es mehr auf Geschwindigkeit als auf ein korrektes Runden ankommt (Physik, Grafik, etc.), kann der performantere Datentyp float eingesetzt werden.

LG,
Roland