Notatka
Dostęp do tej strony wymaga autoryzacji. Może spróbować zalogować się lub zmienić katalogi.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
Math.Round(Double, Int32) i , i MathF.Round(Single, Int32)ich MidpointRounding przeciążenia, teraz zwracają wartość, która jest poprawnie zaokrąglona do żądanej liczby cyfr ułamkowych na podstawie dokładnej wartości danych wejściowych. Niektóre dane wejściowe są teraz zaokrąglone do innego (i poprawnego) wyniku niż w poprzednich wersjach. Ponadto argument nie ma już górnego limitu digits .
Wersja wprowadzona
.NET 11 (wersja zapoznawcza 7)
Poprzednie zachowanie
Math.Round(value, digits, mode) Wcześniej obliczono Round(value * 10^digits, mode) / 10^digitswartość . Ponieważ value * 10^digits zazwyczaj nie można dokładnie przedstawić, wartości nieco poniżej (lub powyżej) punktu dziesiętnego mogą być skalowane w dokładny punkt środkowy lub przez granicę zaokrąglania, co spowodowało niepoprawne zaokrąglenie wyniku. Wartości o dużych wielkościach mogą również utracić ułamkowe bity w całości podczas kroku skalowania.
Argument digits był również ograniczony do 0-15 dla double i 0-6 dla float; wartości poza tym zakresem rzucił ArgumentOutOfRangeException.
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
Na przykład 655.925 jest przechowywany jako 655.924999999999954525…, który znajduje się poniżej punktu środkowego 655.925 , więc prawidłowy wynik to 655.92.
Nowe zachowanie
Począwszy od .NET 11, wynik jest obliczany z dokładnej wartości danych wejściowych przy użyciu arytmetyki dowolnej precyzji, a zwracana wartość jest najbliższą wartością reprezentującą poprawnie zaokrąglony wynik dziesiętny.
Ponadto każda wartość nieujemna digits jest akceptowana; tylko wartości ujemne zgłaszają wartość ArgumentOutOfRangeException. Liczby cyfr z dokładnością wymaganą do zaokrągleń typu (17 dla double, 9 dla float) pozostaw wartość niezmienioną, co jest prawidłowym wynikiem.
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)
Typ zmiany przełamującej
Ta zmiana jest zmianą behawioralną.
Przyczyna zmiany
Poprzednie wyniki były nieprawidłowe dla dużej części danych wejściowych — około 5% wartości losowych w obsługiwanym digits zakresie różniło się od poprawnie zaokrąglonego wyniku. Poprzednie zachowanie również odrzuciło lub błędnie obsłużyło duże skończone dane wejściowe. Nowa implementacja jest spójna na poziomie IEEE: zaokrągla dokładną wartość danych wejściowych i zwraca najbliższy wynik reprezentujący, który odpowiada wartości, która value.ToString("F{digits}") jest już wygenerowana.
Limity 0-15 i 0-6 digits były sztucznym ograniczeniem związanym ze starym podejściem skalowalnym według10^digits . Ponieważ dokładna implementacja jest poprawna dla dowolnej liczby cyfr, limit został zniesiony w tym samym czasie, aby uniknąć drugiej przerwy behawioralnej później.
Aby uzyskać więcej informacji, zobacz dotnet/runtime#130574.
Zalecana akcja
Większość kodu nie wymaga żadnych zmian i korzyści z poprawionych wyników.
Jeśli zależysz od dokładnych poprzednich (niepoprawnych) danych wyjściowych, za pomocą poprzedniego podejścia jawnie, na przykład Math.Round(value * pow10, mode) / pow10. Alternatywnie należy wykonać zaokrąglenie przy użyciu decimal , gdy wartości reprezentują wartości base-10, takie jak waluta.
double i float są binarnymi typami zmiennoprzecinkowymi i nie mogą dokładnie reprezentować większości ułamków dziesiętnych. Aby uzyskać dokładne zaokrąglenie dziesiętne ilości dziesiętne, preferuj wartość Decimal.
<System.Numerics.Decimal32>, <System.Numerics.Decimal64 i <System.Numerics.Decimal128> to typy oparte na przecinku IEEE 754> z rozszerzonymi zakresami i funkcjami, a także nadają się do tego typu pracy.
Interfejsy API, których dotyczy problem
- System.Math.Round(Double, Int32)
- System.Math.Round(Double, Int32, MidpointRounding)
- System.MathF.Round(Single, Int32)
- System.MathF.Round(Single, Int32, MidpointRounding)
To samo poprawione zachowanie i zniesiony digits zakres przepływa przez punkty wejścia interfejsu liczbowego, które delegować do tych metod, na przykład double.Round, , float.RoundHalf.Roundi NFloat.Round.
Math.Round(Decimal, Int32) nie ma to wpływu na pojedyncze argumenty Math.Round(Double) i MathF.Round(Single) przeciążenia.