none
Warum sind die Rundungsergebnisse unterschiedlich? RRS feed

  • Frage

  • Hallo,

    beim Debuggen eines Projektes ist mir eine Ungereimtheiten beim Runden aufgefallen. Ich bekomme bei der Übergabe des gleichen Wertes, mit Unterschiedlichen Wertequellen, ein anderes Ergebnis. Es wird auf 2 Nachkommastellen, AwayFromZero, gerundet.

    Ich habe das Problem mit einem kleinem Testprojekt mal nachgestellt. Der übergende Wert ist 2,625. Erwartet habe ich das Ergebnis 2,63. Bei zwei Ergebnissen bekomme ich aber 2,62 zurück.

    using System;
    
    namespace Rounding
    {
       internal static class Program
       {
          const float Preis = 1f;
          const float Leistung = 4f;
          const float Swk = 5f;
          const float Menge = 2.1f;
    
          [STAThread]
          private static void Main()
          {
             const float constOriginalWert = 2.625f;
             const float constBerechnet = ((1f / Leistung) * Swk) * Preis * Menge;
    
             float _varOriginalWert = 2.625f;
             float _varBerechnet = ((1f / Leistung) * Swk) * Preis * Menge;
    
             var _ergebnisConstOriginalWert = Math.Round(constOriginalWert, 2, MidpointRounding.AwayFromZero);
             var _ergebnisConstBerechnet = Math.Round(constBerechnet, 2, MidpointRounding.AwayFromZero);
             var _ergebnisVarOriginalWert = Math.Round(_varOriginalWert, 2, MidpointRounding.AwayFromZero);
             var _ergebnisVarBerechnet = Math.Round(_varBerechnet, 2, MidpointRounding.AwayFromZero);
             var _direkt = Math.Round(((1f / Leistung) * Swk) * Preis * Menge, 2, MidpointRounding.AwayFromZero);
    
             Console.WriteLine("Berechnungswerte {0}", ((1f / Leistung) * Swk) * Preis * Menge); // 2,625
             Console.WriteLine("---");
             Console.WriteLine("Const Original {0}", constOriginalWert); // 2,625
             Console.WriteLine("Var Original {0}", _varOriginalWert); // 2,625
             Console.WriteLine("Var Berechnet {0}", _varBerechnet); // 2,625
             Console.WriteLine("Const Berechnet {0}", constBerechnet); // 2,625
             Console.WriteLine("\nErwartetes Ergebnis: 2,63");
             Console.WriteLine("----");
             Console.WriteLine("Const Original {0}", _ergebnisConstOriginalWert); // 2,63
             Console.WriteLine("Var Original {0}", _ergebnisVarOriginalWert); // 2,63
             Console.WriteLine("Var Berechnet {0}", _ergebnisVarBerechnet); // 2,63
             Console.WriteLine("\nUnerwartete Ergebnisse");
             Console.WriteLine("---");
             Console.WriteLine("Const Berechnet {0}", _ergebnisConstBerechnet); // 2,62
             Console.WriteLine("Direkt {0}", _direkt); // 2,62
             Console.ReadLine();
          }
       }
    }
    

    Ist jemand in der Lage mir zu erklären woher das Ergebnis 2,62 kommt?

    MfG,
    Stefan

    Dienstag, 25. März 2014 13:31

Antworten

  • Hallo,
    das hier hat nichts mit der Ungenauigkeit zu tun, wie es bei (fast) jeder Gleitkommazahl der Fall ist.

    Du kannst mit der BitConverter-Klasse die Bytes der beiden Werte abrufen:

    Wie du siehst, sind die Werte exakt gleich. Du gibst zwar einen float-Wert an, aber dieser Wert wird implizit in ein double gecastet. Da Konstanten in C# anders gehandhabt werden als in richtigen Compilersprachen (wie C++), fällt auch das Ergebnis leicht anders aus:

    Das Ganze ist, wie ich finde, nicht ganz leicht zu begreifen. Der einfachste Workaround dabei ist die Konstante in eine neue float-Variable zu setzen und dann mit dieser zu arbeiten:

    float newValue = constFloat;
    var e = Math.Round(newValue, 2, MidpointRounding.AwayFromZero);

    Dadurch wird die Konvertierung zu Doubles in beiden fällen gleich ausgeführt.

    Für das Runden der Float-Werte kannst du dir aber auch eine eigene Methode schreiben:

    static float Round(float f, int places, MidpointRounding rounding)
    {
        return (float)Math.Round(f, places, rounding);
    }
    Dadurch wird so oder so zunächst ein normaler float-Wert daraus geformt.


    Koopakiller [kuːpakɪllɐ] (Tom Lambert)
    Webseite | Code Beispiele | Facebook | Twitter | Snippets   C# ↔ VB.NET Konverter
    Markiert bitte beantwortende Posts als Antwort und bewertet Beiträge. Danke.

    Dienstag, 25. März 2014 15:13
    Moderator

Alle Antworten