Baru-baru ini saya harus membuat cerita bersambung ganda menjadi teks, dan kemudian mendapatkannya kembali. Nilai tersebut tampaknya tidak setara:
double d1 = 0.84551240822557006;
string s = d1.ToString("R");
double d2 = double.Parse(s);
bool s1 = d1 == d2;
// -> s1 is False
Tetapi menurut MSDN: Standard Numeric Format Strings , opsi "R" seharusnya menjamin keamanan bolak -balik.
Penentu format round-trip ("R") digunakan untuk memastikan bahwa nilai numerik yang dikonversi ke string akan diuraikan kembali ke dalam nilai numerik yang sama
Kenapa ini terjadi?
Jawaban:
Saya menemukan bug.
.NET melakukan hal berikut dalam
clr\src\vm\comnumber.cpp
:DoubleToNumber
cukup sederhana - itu hanya panggilan_ecvt
, yang ada di runtime C:Ternyata
_ecvt
mengembalikan string845512408225570
.Perhatikan nol yang tertinggal? Ternyata itu membuat semua perbedaan!
Ketika nol hadir, hasilnya benar-benar diuraikan kembali
0.84551240822557006
, yang merupakannomor asli Anda- jadi itu sama, dan karenanya hanya 15 digit yang dikembalikan.Namun, jika saya memotong string pada nol ke
84551240822557
, maka saya kembali0.84551240822556994
, yang bukan nomor asli Anda, dan karenanya akan mengembalikan 17 digit.Bukti: jalankan kode 64-bit berikut (sebagian besar yang saya ekstrak dari Microsoft Shared Source CLI 2.0) di debugger Anda dan periksa
v
di akhirmain
:sumber
+1
. Kode ini dari shared-source-cli-2.0 kan? Ini adalah satu-satunya pemikiran yang saya temukan.Sepertinya saya ini hanyalah bug. Harapan Anda sepenuhnya masuk akal. Saya telah mereproduksinya menggunakan .NET 4.5.1 (x64), menjalankan aplikasi konsol berikut yang menggunakan
DoubleConverter
kelas saya .DoubleConverter.ToExactString
menunjukkan nilai persis yang diwakili olehdouble
:Hasil dalam .NET:
Hasil dalam Mono 3.3.0:
Jika Anda secara manual menentukan string dari Mono (yang berisi "006" di akhir), .NET akan mem-parsing itu kembali ke nilai aslinya. Kelihatannya masalahnya ada di
ToString("R")
handling bukan pada parsing.Seperti disebutkan dalam komentar lain, sepertinya ini khusus untuk berjalan di bawah CLR x64. Jika Anda mengkompilasi dan menjalankan penargetan kode di atas x86, tidak apa-apa:
... Anda mendapatkan hasil yang sama dengan Mono. Akan menarik untuk mengetahui apakah bug itu muncul di bawah RyuJIT - Saya sendiri belum menginstalnya. Secara khusus, saya bisa membayangkan ini mungkin menjadi bug JIT, atau sangat mungkin bahwa ada implementasi yang berbeda dari internal
double.ToString
berdasarkan arsitektur.Saya sarankan Anda mengajukan bug di http://connect.microsoft.com
sumber
ToString()
? Ketika saya mencoba mengganti nilai kode keras denganrand.NextDouble()
dan tidak ada masalah.ToString("R")
konversi. CobaToString("G32")
dan perhatikan itu mencetak nilai yang benar.Baru-baru ini, saya mencoba menyelesaikan masalah ini . Seperti yang ditunjukkan melalui kode , double.ToString ("R") memiliki logika berikut:
Dalam hal ini, double.ToString ("R") salah memilih hasil dalam presisi 15 sehingga bug terjadi. Ada solusi resmi di dokumen MSDN:
Jadi, kecuali masalah ini diselesaikan, Anda harus menggunakan double.ToString ("G17") untuk bolak-balik.
Pembaruan : Sekarang ada masalah khusus untuk melacak bug ini.
sumber