C#: Gleitkommapräzision und Rundung

Was für ein Titel! 😀

Aber es ist durchaus einen Beitrag wert, sich mit der Präzision und Rundung von Gleitkommazahlen in C# auseinanderzusetzen. Gerade, wenn man (wie ich) an einem finanzmathematischen Programm schreibt, kann es sonst sehr schnell zu Problemen bzw. seltsamen Ergebnissen kommen. Da ich diese Erfahrung heute machen durfte (und ca. 50 Variablen von double auf decimal umstellen musste), dachte ich mir: Erleuchte die Nachwelt! 😉

Nun denn, ein Beispiel zum Einstieg. Man könnte meinen, 1000 * 0,3 seien 300, was auch richtig wäre. Aber C# behauptet, das Ergebnis sei ein klein wenig anders. Das folgende kurze Programm gibt den Wert in nachstehendem Screenshot aus.
static void Main(string[] args) { double x = 0; for (int i = 1; i <= 1000; i++) { x += 0.3; } Console.WriteLine(x.ToString()); Console.ReadLine(); }
Screenshot: Rechnen mit Gleitkommazahlen in C#

Bevor ich mich an einer Erklärung versuche, verlinke ich lieber auf eine sehr gute vorhandene: Fließkommadarstellung und Problembehandlung.

Nachdem ich das verdaut hatte, durfte ich also wie erwähnt meine gesamten Variablen auf decimal umstellen. Aber dafür lieferte mein Progrämmchen nun auch das richtige Ergebnis… nun, nicht ganz.

Leider gab es bei der Addition von 1400 (Test-)Werten genau 1 Cent Differenz. Nach einer (zugegeben aufwändigen und langwierigen Suche) konnte ich den Datensatz ausmachen, der den Fehler verursachte. Nur leider war der “Fehler” eine Eigenschaft der Methode Math.Round(), die ich zum Berechnen des Wertes eingesetzt habe. Diese rundet nämlich bei .5 nicht (kaufmännisch) auf, sondern ab (Eigentlich rundet sie noch ein wenig anders: immer zur nächsten geraden Zahl). Der nachfolgende Screenshot zeigt die seltsame Ausgabe dieses kleinen Programms:
static void Main(string[] args) { for (int i = 1; i <= 10; i++) { double x = 0.505 + i * 0.01; Console.WriteLine(x + " gerundet: " + Math.Round(x, 2)); } Console.ReadLine(); }
Screenshot: Rundung in C#

Nun denn. Den “Fehler” konnte ich recht einfach beheben, indem ich Math.Round() noch einen dritten Parameter mitgegeben habe: MidpointRounding.AwayFromZero. Danach, wird aus Math.Round(0.5, 2, MidpointRounding.AwayFromZero) auch tatsächlich 1.

Über Stefan

Polyglot Clean Code Developer

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert

To create code blocks or other preformatted text, indent by four spaces:

    This will be displayed in a monospaced font. The first four 
    spaces will be stripped off, but all other whitespace
    will be preserved.
    
    Markdown is turned off in code blocks:
     [This is not a link](http://example.com)

To create not a block, but an inline code span, use backticks:

Here is some inline `code`.

For more help see http://daringfireball.net/projects/markdown/syntax