Math.Round dan MathF.Round mengembalikan hasil yang dibulatkan dengan benar

Math.Round(Double, Int32) dan MathF.Round(Single, Int32), dan kelebihan bebannya MidpointRounding , sekarang mengembalikan nilai yang dibulatkan dengan benar ke jumlah digit pecahan yang diminta berdasarkan nilai input yang tepat . Beberapa input sekarang membulatkan ke hasil yang berbeda (dan benar) daripada rilis sebelumnya. Selain itu, digits argumen tidak lagi memiliki batas atas.

Versi yang diperkenalkan

.NET 11 Pratinjau 7

Perilaku sebelumnya

Sebelumnya, Math.Round(value, digits, mode) komputasi Round(value * 10^digits, mode) / 10^digits. Karena value * 10^digits umumnya tidak dapat direpresentasikan secara tepat, nilai yang sedikit di bawah (atau di atasnya) titik tengah desimal dapat diskalakan ke titik tengah yang tepat atau di seluruh batas pembulatan, yang menghasilkan hasil yang salah dibulatkan. Nilai dengan besarnya juga dapat kehilangan bit pecahannya sepenuhnya selama langkah penskalaan.

Argumen digits juga dibatasi hingga 0-15 untuk double dan 0-6 untuk float; nilai di luar rentang tersebut melemparkan 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

Misalnya, 655.925 disimpan sebagai 655.924999999999954525…, yang berada di bawah 655.925 titik tengah, sehingga hasil yang benar adalah 655.92.

Perilaku baru

Mulai .NET 11, hasilnya dihitung dari nilai input yang tepat menggunakan aritmatika presisi arbitrer, dan nilai yang dikembalikan adalah nilai terdekat yang dapat direpresentasikan ke hasil desimal yang dibulatkan dengan benar.

Selain itu, nilai non-negatif digits apa pun diterima; hanya nilai negatif yang melemparkan ArgumentOutOfRangeException. Jumlah digit pada atau di luar presisi yang diperlukan untuk melakukan perjalanan pulang pergi jenis (17 untuk double, 9 untuk float) membiarkan nilai tidak berubah, yang merupakan hasil yang benar.

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)

Jenis perubahan yang memutus kompatibilitas

Perubahan ini adalah perubahan perilaku.

Alasan perubahan

Hasil sebelumnya salah untuk sebagian besar input—kira-kira 5% nilai acak di seluruh rentang yang didukung digits berbeda dari hasil yang dibulatkan dengan benar. Perilaku sebelumnya juga menolak atau salah penanganan input terbatas besar. Implementasi baru konsisten dengan IEEE: ini membulatkan nilai input yang tepat dan mengembalikan hasil terdekat yang dapat diwakili, yang cocok dengan nilai yang value.ToString("F{digits}") sudah dihasilkan.

Topi 0-15 dan 0-6 digits adalah batasan buatan yang terkait dengan pendekatan skala lama demi10^digits -. Karena implementasi yang tepat benar untuk jumlah digit apa pun, batas diangkat pada saat yang sama untuk menghindari jeda perilaku kedua nanti.

Untuk informasi selengkapnya, lihat dotnet/runtime#130574.

Sebagian besar kode tidak memerlukan perubahan dan manfaat dari hasil yang dikoreksi.

Jika Anda bergantung pada output yang tepat sebelumnya (salah), bulatkan menggunakan pendekatan sebelumnya secara eksplisit, misalnya, Math.Round(value * pow10, mode) / pow10. Atau, lakukan pembulatan menggunakan decimal saat nilai mewakili kuantitas dasar-10 seperti mata uang.

double dan float merupakan jenis titik mengambang biner dan tidak dapat secara tepat mewakili sebagian besar pecahan desimal. Untuk pembulatan desimal yang tepat dari jumlah desimal, pilih Decimal. <System.Numerics.Decimal32>, <System.Numerics.Decimal64>, dan <System.Numerics.Decimal128> adalah jenis berbasis desimal IEEE 754 dengan rentang dan fungsionalitas yang diperluas, dan juga cocok untuk jenis pekerjaan ini.

API yang Terpengaruh

Perilaku yang dikoreksi yang sama dan alur rentang yang diangkat digits melalui titik entri antarmuka numerik yang mendelegasikan ke metode ini, misalnya, , double.Roundfloat.Round, Half.Round, dan NFloat.Round. Math.Round(Decimal, Int32) dan argumen Math.Round(Double) tunggal dan MathF.Round(Single) kelebihan beban tidak terpengaruh.