2016-06-25 11 views
-1

Ich versuche, eine Zahl zu 7 Dezimalstellen abzurunden, aber ich bemerkte, dass Math.Round nicht richtig mit einigen Zahlen funktioniert:C# Math.Round Bug?

Math.Round(39.248779999999996,3) => 39.249 
Math.Round(39.248779999999996,4) => 39.2488 
Math.Round(39.248779999999996,5) => 39.248779999999996 
Math.Round(39.248779999999996,6) => 39.248779999999996 
Math.Round(39.248779999999996,7) => 39.248779999999996 

Kann mir jemand erklären dieses Verhalten?

+7

Diese sind Gleitkommawerte . Sie haben keine endliche Binärdarstellung mit einer beliebigen Anzahl von Ziffern. Konvertieren Sie es in einen 'Dezimaltyp ', wenn Sie eine genaue Darstellung wünschen. –

+0

Das Problem ist, dass ich ein Double an eine externe Komponente übergeben muss, für die ich die Methodensignatur nicht ändern kann, und dass diese Komponente einen Fehler auslöst, wenn die Zahl mehr als 7 Dezimalstellen hat. Gibt es eine Möglichkeit, diese doppelte Zahl effektiv auf 7 Dezimalstellen zu runden? – smeegoan

+0

Ähm, es gibt keine Möglichkeit für einen Double, einen gerundeten Wert zu speichern, damit hätte das eh nicht funktioniert. Die externe Komponente ist fehlerhaft. Doppel haben keine feste Anzahl von Dezimalstellen. Es hätte den Dezimaltyp verwenden sollen. –

Antwort

4

Wenn Sie Präzision benötigen, verwenden Sie decimal und nicht double/float;

var num = 39.248779999999996; // num is double. 
var num = 39.248779999999996m; // num is decimal. 

Das dezimale Schlüsselwort gibt einen 128-Bit-Datentyp. Im Vergleich zu Gleitkommatypen hat der Dezimaltyp eine höhere Genauigkeit und ein kleinerer Bereich, was ihn für finanzielle und monetäre Berechnungen geeignet macht.

Edit:

Sie can't alle Zahlen genau in float/double darstellen:

Binary Floating-Point-Arithmetik ist in Ordnung, solange Sie wissen, was auf geht und nicht erwarten Sie, dass die Werte genau die Dezimalstellen sind, die Sie in Ihr Programm eingegeben haben, und erwarten Sie keine Berechnungen mit binären Gleitkommazahlen, die notwendigerweise genaue Ergebnisse liefern. Auch wenn zwei Zahlen genau in dem Typ, den Sie verwenden, dargestellt werden, wird das Ergebnis einer Operation, die diese beiden Zahlen enthält, nicht unbedingt genau dargestellt. Dies ist am einfachsten mit Division (zB 1/10 ist nicht genau darstellbar, obwohl sowohl 1 und 10 genau darstellbar) aber es kann mit jeder Operation passieren - auch scheinbar unschuldige wie Addition und Subtraktion.

Beispiel:

double doubleValue = 1f/10f; // => 0.10000000149011612 
decimal decimalValue = 1m/10m; // => 0.1 

Sie die Ziffern verkürzen können maximal 7 Stellen zu gewährleisten, aber man kann nicht genau um den Wert:

double value = 39.248779999999996; 

double roundTo = Math.Pow(10, 7); 
double resultResult = Math.Truncate(value * roundTo)/roundTo; 
// result is : 39.2487799 
+0

Das Problem ist, ich muss eine doppelte an eine externe Komponente übergeben, für die ich nicht ändern kann, die Methode Signatur und diese Komponente wird einen Fehler werfen, wenn die Zahl mehr als 7 Dezimalstellen hat. Gibt es eine Möglichkeit, diese doppelte Zahl effektiv auf 7 Dezimalstellen zu runden? – smeegoan

+0

@smeegoan Überprüfen Sie die bearbeitete Antwort oben. – user3185569

+0

Ich habe das versucht und das Ergebnis bleibt gleich 39.248779999999996. Wenn Sie Console.WriteLine zum Anzeigen des Ergebnisses verwendet haben, vergewissern Sie sich, dass Sie 'Console.WriteLine (result.ToString (" r "));' verwendet haben, da standardmäßig der allgemeine Formatbezeichner ("G") verwendet wird (https: // msdn .Microsoft.com/de-us/library/dwhawy9k.aspx # RFormatString) – smeegoan