Catatan
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba masuk atau mengubah direktori.
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba mengubah direktori.
Konversi antara Decimal dan tipe titik-mengambang biner, serta konversi dari BigInteger ke tipe titik-mengambang biner, kini menghasilkan hasil yang dibulatkan secara tepat. Sebelumnya, konversi ini dapat memotong, membulatkan melalui nilai perantara, atau membuang digit signifikan.
Versi yang diperkenalkan
.NET 11 Preview 7
Perilaku sebelumnya
Konversi dari float dan double ke decimal hanya dipertahankan masing-masing 7 dan 15 digit desimal yang signifikan. Ini dapat menyembunyikan perbedaan antara literal biner bertitik mengambang dan literal desimal:
using System.Globalization;
double value = 1.23;
decimal converted = (decimal)value;
Console.WriteLine(converted.ToString("G29", CultureInfo.InvariantCulture));
Outputnya adalah:
1.23
Konversi dari decimal ke float atau double dapat mengalami pembulatan lebih dari sekali. Contohnya:
using System.Globalization;
decimal value = 10000000000000.099609375m;
double converted = (double)value;
Console.WriteLine(converted.ToString("G99", CultureInfo.InvariantCulture));
Outputnya adalah:
10000000000000.09765625
Konversi dari decimal ke Half atau BFloat16 mula-mula mengonversi nilai tersebut menjadi float, yang juga dapat menghasilkan hasil pembulatan yang keliru.
Konversi dari BigInteger ke double membuang bit yang terpotong alih-alih membulatkan ke nilai terdekat yang dapat diwakili. Contohnya:
using System.Globalization;
using System.Numerics;
BigInteger value = long.MaxValue / 2;
double converted = (double)value;
Console.WriteLine(converted.ToString("G17", CultureInfo.InvariantCulture));
Outputnya adalah:
4.6116860184273874E+18
Konversi dari BigInteger ke float, Half, atau BFloat16 pertama-tama mengonversi nilai menjadi double, yang dapat membulatkan dua kali dan menghasilkan hasil satu unit di tempat terakhir jauh dari nilai terdekat yang dapat diwakili.
Perilaku baru
Mulai dari .NET 11, konversi membulatkan nilai sumber eksak satu kali ke nilai tujuan terdekat yang dapat direpresentasikan.
Untuk contoh pertama, double nilainya tidak persis 1.23. Nilai pastinya sekitar 1.229999999999999982236431605997..., sehingga nilai yang dikonversi decimal sekarang:
1.229999999999999982236431606
Untuk contoh kedua, nilai yang dikonversi double sekarang:
10000000000000.099609375
Konversi dari decimal ke Half atau BFloat16 juga dibulatkan dengan benar dan dapat menghasilkan pola bit tujuan yang berbeda dari versi sebelumnya.
Untuk contoh BigInteger, nilai double yang dikonversi sekarang adalah:
4.6116860184273879E+18
Konversi dari BigInteger ke float, Half, atau BFloat16 juga dibulatkan dengan benar.
Jika konversi dievaluasi sebagai konstanta waktu kompilasi, kompilator yang dihosting oleh SDK Pratinjau 7 .NET 11 atau SDK yang lebih baru dapat menyematkan hasil baru dalam perakitan output saat proyek dibangun kembali, terlepas dari kerangka kerja target proyek.
Jenis perubahan yang memutus kompatibilitas
Perubahan ini adalah perubahan perilaku.
Alasan perubahan
Algoritma konversi sebelumnya kehilangan informasi yang dapat diwakili oleh jenis tujuan dan terkadang memilih nilai selain hasil terdekat yang dapat diwakili. Hal ini menyebabkan kesalahan presisi dalam kedua arah konversi decimal serta dalam konversi dari BigInteger. Algoritma baru mengikuti aturan bilangan floating-point yang diharapkan untuk perhitungan konversi, seolah-olah menggunakan presisi perantara yang eksak, lalu diikuti oleh satu kali pembulatan ke tipe tujuan.
Untuk informasi selengkapnya, lihat dotnet/runtime#130565 dan dotnet/runtime#130566.
Tindakan yang disarankan
Jangan berasumsi bahwa literal floating-point biner dan literal desimal dengan teks kode sumber yang sama merepresentasikan nilai yang sama. Jika suatu nilai dimaksudkan sebagai bilangan desimal, gunakan literal desimal:
decimal value = 123.4567m;
alih-alih konversi dari double harfiah:
decimal value = (decimal)123.4567;
Untuk memulihkan hasil sebelumnya secara umum saat Anda mengonversi ke decimal, bulatkan nilai yang dikonversi menjadi 7 digit desimal signifikan untuk float sumber atau 15 digit desimal signifikan untuk double sumber. Konversi sebelumnya dibulatkan ke nilai terdekat, dengan nilai tengah dibulatkan ke genap; bukan dipotong. Nilai positif dan negatif ditangani secara simetris.
Misalnya, memformat nilai sumber dengan string format numerik standar G7 atau G15 lalu mengurai hasilnya akan melakukan pembulatan digit signifikan yang sesuai untuk nilai hingga yang sembarang:
using System.Globalization;
static decimal ConvertToDecimalLikePrevious(float value)
{
Span<char> text = stackalloc char[32];
value.TryFormat(text, out int length, "G7", CultureInfo.InvariantCulture);
return decimal.Parse(text[..length], NumberStyles.Float, CultureInfo.InvariantCulture);
}
static decimal ConvertToDecimalLikePrevious(double value)
{
Span<char> text = stackalloc char[32];
value.TryFormat(text, out int length, "G15", CultureInfo.InvariantCulture);
return decimal.Parse(text[..length], NumberStyles.Float, CultureInfo.InvariantCulture);
}
decimal fromFloat = ConvertToDecimalLikePrevious(14.1f); // 14.1
decimal fromDouble = ConvertToDecimalLikePrevious(-123.4567); // -123.4567
Implementasi berbasis rentang tidak mengalokasikan. Nilai di luar rentang decimal terus melempar pengecualian selama penguraian, seperti yang mereka lakukan selama konversi.
Perbarui pengujian dan nilai harapan yang diserialisasi yang mengodekan hasil sebelumnya yang dibulatkan secara keliru. Ini termasuk kode yang bergantung pada trunkasi konversi BigInteger ke nol. Jika aplikasi memerlukan pola warisan float, , double, Halfatau BFloat16 bit tertentu untuk protokol atau format file, kodekan pola bit tersebut secara eksplisit daripada mereproduksinya melalui konversi numerik.
Tidak ada sakelar kompatibilitas untuk memulihkan algoritma konversi sebelumnya.
API yang Terpengaruh
- Decimal.Decimal(Single)
- Decimal.Decimal(Double)
- Konversi eksplisit dari Single dan Double ke Decimal
- Konversi eksplisit dari Decimal ke Single dan Double
- Decimal.ToSingle(Decimal)
- Decimal.ToDouble(Decimal)
- Convert.ToDecimal(Single)
- Convert.ToDecimal(Double)
- Convert.ToSingle(Decimal)
- Convert.ToDouble(Decimal)
- Decimal.CreateChecked<TOther>(TOther), Decimal.CreateSaturating<TOther>(TOther), dan Decimal.CreateTruncating<TOther>(TOther) saat dikonversi ke atau dari Single atau Double
- Konversi eksplisit dari Decimal ke Half
- Konversi eksplisit dari Decimal ke BFloat16
- Konversi eksplisit dari BigInteger ke Double, Single, Half, dan BFloat16
- Konversi setara yang dilakukan melalui IConvertible atau antarmuka matematika generik