Math.Round und MathF.Round geben korrekt gerundete Ergebnisse zurück.

Math.Round(Double, Int32) und MathF.Round(Single, Int32), und ihre MidpointRounding Überladungen, geben nun den Wert zurück, der korrekt auf die angeforderte Anzahl von Dezimalstellen gerundet ist, basierend auf dem genauen Wert der Eingabe. Einige Eingaben runden jetzt auf ein anderes (und korrektes) Ergebnis als in früheren Versionen ab. Darüber hinaus weist das digits Argument keine Obergrenze mehr auf.

Eingeführt in Version

.NET 11 Vorschau 7

Bisheriges Verhalten

Math.Round(value, digits, mode) Zuvor berechnetRound(value * 10^digits, mode) / 10^digits. Da value * 10^digits die Werte im Allgemeinen nicht genau dargestellt werden können, könnte ein Dezimalmittelpunkt leicht unter (oder höher) in einen genauen Mittelpunkt oder über eine abgerundete Grenze skaliert werden, was zu einem falsch gerundeten Ergebnis führt. Werte mit großen Größen könnten auch ihre Bruchbits vollständig während des Skalierungsschritts verlieren.

Das digits Argument war auch auf 0-15 für double und 0-6 für float; Werte außerhalb dieses Bereichs ArgumentOutOfRangeExceptionbeschränkt.

Math.Round(655.925, 2, MidpointRounding.AwayFromZero);            // 655.93              (incorrect)
Math.Round(1111111111111111.5, 1, MidpointRounding.AwayFromZero); // 1111111111111111.6  (incorrect)
Math.Round(1.5, 16, MidpointRounding.ToEven);                     // throws ArgumentOutOfRangeException

Beispielsweise 655.925 wird als 655.924999999999954525…, die unter dem 655.925 Mittelpunkt liegt, gespeichert, sodass das richtige Ergebnis ist 655.92.

Neues Verhalten

Ab .NET 11 wird das Ergebnis anhand des genauen Werts der Eingabe mit Arithmetik mit beliebiger Genauigkeit berechnet, und der zurückgegebene Wert ist der nächste darstellbare Wert für das korrekt gerundete Dezimalergebnis.

Außerdem wird jeder nicht negative digits Wert akzeptiert; nur negative Werte werden ausgelöst ArgumentOutOfRangeException. Ziffernzählungen mit oder über der Genauigkeit, die zum Runden des Typs (17 für double, 9 für float) erforderlich sind, lassen den Wert unverändert, was das richtige Ergebnis ist.

Math.Round(655.925, 2, MidpointRounding.AwayFromZero);            // 655.92              (correct)
Math.Round(1111111111111111.5, 1, MidpointRounding.AwayFromZero); // 1111111111111111.5  (correct)
Math.Round(1.5, 16, MidpointRounding.ToEven);                     // 1.5                 (no longer throws)

Art der einschneidenden Änderung

Diese Änderung ist eine Verhaltensänderung.

Grund für die Änderung

Die vorherigen Ergebnisse waren für einen großen Anteil von Eingaben falsch – etwa 5% zufälliger Werte im unterstützten digits Bereich unterscheiden sich vom korrekt gerundeten Ergebnis. Das vorherige Verhalten hat auch große endliche Eingaben abgelehnt oder falsch behandelt. Die neue Implementierung ist IEEE-konsistent: Sie rundet den genauen Wert der Eingabe und gibt das nächste darstellbare Ergebnis zurück, das dem bereits erzeugten Wert value.ToString("F{digits}") entspricht.

Die 0-15- und 0-6-Kappen digits waren eine künstliche Einschränkung, die an den alten Scale-by-Ansatz10^digits gebunden ist. Da die genaue Implementierung für jede Ziffernanzahl korrekt ist, wurde die Feststelltaste gleichzeitig aufgehoben, um später eine zweite Verhaltensunterbrechung zu vermeiden.

Weitere Informationen finden Sie unter dotnet/runtime#130574.

Die meisten Code benötigt keine Änderung und profitiert von den korrigierten Ergebnissen.

Wenn Sie von der genauen vorherigen (falschen) Ausgabe abhängen, runden Sie den vorherigen Ansatz explizit ab, Math.Round(value * pow10, mode) / pow10z. B. . Führen Sie alternativ die Rundung aus decimal , wenn die Werte Basis-10-Mengen wie Währung darstellen.

double und float sind binäre Gleitkommatypen und können nicht genau die meisten Dezimalbrüche darstellen. Bei exakter Dezimalrundeung von Dezimalmengen bevorzugen DecimalSie . <System.Numerics.Decimal32>, <System.Numerics.Decimal64> und <System.Numerics.Decimal128> sind IEEE 754 dezimalbasierte Typen mit erweiterten Bereichen und Funktionen und eignen sich auch für diese Art von Arbeit.

Betroffene APIs

Dasselbe korrigierte Verhalten und ein aufgehobener digits Bereich fließen durch die numerischen Schnittstelleneinstiegspunkte, die an diese Methoden delegieren, double.Roundz. B. , , float.Round, Half.Roundund NFloat.Round. Math.Round(Decimal, Int32) und die Ein-Argument Math.Round(Double) - und MathF.Round(Single) Überladungen sind nicht betroffen.