Ketika serialisasi dan deserializing nilai antara JavaScript dan C # menggunakan SignalR dengan MessagePack saya melihat sedikit kehilangan presisi dalam C # di sisi penerima.
Sebagai contoh, saya mengirim nilai 0,005 dari JavaScript ke C #. Ketika nilai deserialized muncul di sisi C # saya mendapatkan nilai 0.004999999888241291
, yang dekat, tetapi tidak tepat 0,005. Nilai di sisi JavaScript adalah Number
dan di sisi C # yang saya gunakan double
.
Saya telah membaca bahwa JavaScript tidak dapat mewakili angka floating point persis yang dapat menyebabkan hasil seperti 0.1 + 0.2 == 0.30000000000000004
. Saya menduga masalah yang saya lihat terkait dengan fitur JavaScript ini.
Bagian yang menarik adalah bahwa saya tidak melihat masalah yang sama terjadi sebaliknya. Mengirim 0,005 dari C # ke JavaScript menghasilkan nilai 0,005 dalam JavaScript.
Sunting : Nilai dari C # disingkat di jendela debugger JS. Seperti @Pete sebutkan itu memperluas ke sesuatu yang tidak tepat 0,5 (0,0050000000000000000004040404058) Ini berarti perbedaan setidaknya terjadi di kedua sisi.
Serialisasi JSON tidak memiliki masalah yang sama karena saya mengasumsikannya berjalan melalui string yang membuat lingkungan penerima dalam kontrol wrt mengurai nilai ke dalam tipe numerik aslinya.
Saya bertanya-tanya apakah ada cara menggunakan serialisasi biner untuk memiliki nilai yang cocok di kedua sisi.
Jika tidak, apakah ini berarti bahwa tidak ada cara untuk memiliki konversi biner 100% akurat antara JavaScript dan C #?
Teknologi yang digunakan:
- JavaScript
- .Net Core dengan SignalR dan msgpack5
Kode saya didasarkan pada posting ini . Satu-satunya perbedaan adalah bahwa saya menggunakan ContractlessStandardResolver.Instance
.
Jawaban:
MEMPERBARUI
Ini telah diperbaiki pada rilis berikutnya (5.0.0-preview4) .
Jawaban Asli
Saya menguji
float
dandouble
, dan yang menarik dalam kasus khusus ini, hanyadouble
punya masalah, sedangkanfloat
tampaknya berfungsi (yaitu 0,005 dibaca di server).Memeriksa byte pesan menyarankan bahwa 0,005 dikirim sebagai tipe
Float32Double
yang merupakan nomor 4-byte / 32-bit floating point presisi tunggal IEEE 754 meskipunNumber
64 bit floating point.Jalankan kode berikut di konsol yang mengkonfirmasi hal di atas:
mspack5 memang menyediakan opsi untuk memaksa floating point 64 bit:
Namun,
forceFloat64
opsi ini tidak digunakan oleh signalr-protokol-msgpack .Meskipun itu menjelaskan mengapa
float
bekerja di sisi server, tetapi sebenarnya tidak ada perbaikan untuk itu sampai sekarang . Mari kita tunggu apa yang dikatakan Microsoft .Kemungkinan solusi
forceFloat64
default to true ?? Saya tidak tahufloat
di sisi serverstring
di kedua sisidecimal
sisi server dan tulis kustomIFormatterProvider
.decimal
bukan tipe primitif, danIFormatterProvider<decimal>
dipanggil untuk properti tipe kompleksdouble
nilai properti dan lakukan trikdouble
->float
->decimal
->double
TL; DR
Masalah dengan klien JS yang mengirim nomor floating point tunggal ke C # backend menyebabkan masalah floating point yang diketahui:
Untuk penggunaan langsung
double
metode, masalah dapat diselesaikan dengan kebiasaanMessagePack.IFormatterResolver
:Dan gunakan resolver:
Penyelesai tidak sempurna, karena casting
decimal
untukdouble
memperlambat proses dan itu bisa berbahaya .Namun
Seperti OP yang ditunjukkan dalam komentar, ini tidak dapat menyelesaikan masalah jika menggunakan tipe kompleks yang memiliki
double
properti yang dikembalikan.Investigasi lebih lanjut mengungkapkan penyebab masalah di MessagePack-CSharp:
Dekoder di atas digunakan ketika perlu mengkonversi satu
float
nomor kedouble
:v2
Masalah ini ada di versi v2 MessagePack-CSharp. Saya telah mengajukan masalah pada github , meskipun masalah ini tidak akan diperbaiki .
sumber
float
solusi. Saya tidak tahu apakah mereka sudah memperbaikinya di v2. Saya akan melihat sekali saya punya waktu. Namun, masalahnya v2 belum kompatibel dengan SignalR. Hanya versi pratinjau (5.0.0.0- *) dari SignalR yang dapat menggunakan v2.Silakan periksa nilai tepat yang Anda kirim ke presisi yang lebih besar. Bahasa biasanya membatasi presisi pada cetakan untuk membuatnya terlihat lebih baik.
sumber