Podczas szeregowania i deserializacji wartości między JavaScript i C # za pomocą SignalR z MessagePack widzę trochę utraty precyzji w C # na końcu odbierającym.
Jako przykład przesyłam wartość 0,005 z JavaScript do C #. Gdy po stronie C # pojawia się deserializowana wartość, otrzymuję wartość 0.004999999888241291
, która jest bliska, ale nie dokładnie 0,005. Wartość po stronie JavaScript to Number
i po stronie C # używam double
.
Czytałem, że JavaScript nie może dokładnie reprezentować liczb zmiennoprzecinkowych, co może prowadzić do podobnych wyników 0.1 + 0.2 == 0.30000000000000004
. Podejrzewam, że problem, który widzę, jest związany z tą funkcją JavaScript.
Interesujące jest to, że nie widzę tego samego problemu idącego w drugą stronę. Przesłanie 0,005 z C # do JavaScript skutkuje wartością 0,005 w JavaScript.
Edycja : Wartość z C # jest właśnie skrócona w oknie debugera JS. Jak wspomniano @Pete, rozwija się do czegoś, co nie jest dokładnie 0,5 (0,005000000000000000104083408558). Oznacza to, że rozbieżność zdarza się przynajmniej po obu stronach.
Serializacja JSON nie ma tego samego problemu, ponieważ zakładam, że przechodzi ona przez łańcuch znaków, który pozostawia środowisko odbiorcze w kontroli parsującej wartość do jej rodzimego typu numerycznego.
Zastanawiam się, czy istnieje sposób użycia serializacji binarnej, aby uzyskać pasujące wartości po obu stronach.
Jeśli nie, to czy to oznacza, że nie ma możliwości uzyskania 100% dokładnych konwersji binarnych między JavaScript i C #?
Zastosowana technologia:
- JavaScript
- .Net Core z SignalR i msgpack5
Mój kod oparty jest na tym poście . Jedyna różnica polega na tym, że używam ContractlessStandardResolver.Instance
.
Odpowiedzi:
AKTUALIZACJA
Zostało to naprawione w następnej wersji (5.0.0-Preview4) .
Oryginalna odpowiedź
Testowałem
float
idouble
, co ciekawe, w tym konkretnym przypadku,double
miałem tylko problem, podczas gdyfloat
wydaje się, że działa (tj. 0,005 jest odczytywany na serwerze).Sprawdzanie bajtów wiadomości sugerowało, że 0,005 jest wysyłany jako typ,
Float32Double
który jest 4-bajtową / 32-bitową liczbą zmiennoprzecinkową pojedynczej precyzji IEEE 754, pomimo żeNumber
jest liczbą zmiennoprzecinkową 64-bitową.Uruchom następujący kod w konsoli potwierdził powyższe:
mspack5 zapewnia opcję wymuszenia 64-bitowego zmiennoprzecinkowego:
Jednak
forceFloat64
opcja ta nie jest używana przez msgpack signalr-protokół-protokół .Chociaż to wyjaśnia, dlaczego
float
działa po stronie serwera, ale tak naprawdę nie ma na to obecnie żadnej poprawki . Poczekajmy, co mówi Microsoft .Możliwe obejścia
forceFloat64
domyślną wartością true? Nie wiemfloat
po stronie serwerastring
po obu stronachdecimal
na stronę serwera i napisz niestandardoweIFormatterProvider
.decimal
nie jest typem pierwotnym iIFormatterProvider<decimal>
jest wywoływany dla właściwości typu złożonegodouble
wartości właściwości i wykonaj sztuczkędouble
->float
->decimal
->double
TL; DR
Problem z wysyłaniem przez klienta JS pojedynczej liczby zmiennoprzecinkowej do zaplecza C # powoduje znany problem zmiennoprzecinkowy:
W przypadku bezpośredniego użycia
double
metod, problem można rozwiązać niestandardowoMessagePack.IFormatterResolver
:I użyj resolvera:
Resolver nie jest doskonały, jak do odlewania
decimal
następniedouble
spowalnia proces w dół, a to może być niebezpieczne .jednak
Jak wskazał PO w komentarzach, nie można rozwiązać tego problemu, jeśli używa się złożonych typów o
double
zwracanych właściwościach.Dalsze dochodzenie ujawniło przyczynę problemu w MessagePack-CSharp:
Powyższy dekoder jest używany, gdy trzeba przekonwertować pojedynczy
float
numer nadouble
:v2
Ten problem występuje w wersjach MessagePack-CSharp v2. Złożyłem problem na githubie , ale problem nie zostanie naprawiony .
źródło
float
obejścia. Nie wiem, czy naprawili to w wersji 2. Przyjrzę się, kiedy będę miał trochę czasu. Problem polega jednak na tym, że wersja 2 nie jest jeszcze kompatybilna z SignalR. Tylko wersje zapoznawcze SignalR (5.0.0.0- *) mogą używać v2.Sprawdź dokładną wartość, którą wysyłasz, z większą precyzją. Języki zazwyczaj ograniczają precyzję wydruku, aby wyglądał lepiej.
źródło