Beim Serialisieren und Deserialisieren von Werten zwischen JavaScript und C # mithilfe von SignalR mit MessagePack tritt auf der Empfangsseite ein gewisser Genauigkeitsverlust in C # auf.
Als Beispiel sende ich den Wert 0,005 von JavaScript an C #. Wenn der deserialisierte Wert auf der C # -Seite angezeigt wird, erhalte ich den Wert 0.004999999888241291
, der nahe beieinander liegt, aber nicht genau 0,005. Der Wert auf der JavaScript-Seite ist Number
und auf der C # -Seite, die ich verwende double
.
Ich habe gelesen, dass JavaScript Gleitkommazahlen nicht genau darstellen kann, was zu Ergebnissen wie führen kann 0.1 + 0.2 == 0.30000000000000004
. Ich vermute, dass das Problem, das ich sehe, mit dieser Funktion von JavaScript zusammenhängt.
Der interessante Teil ist, dass ich nicht sehe, dass das gleiche Problem in die andere Richtung geht. Das Senden von 0,005 von C # an JavaScript führt zu dem Wert 0,005 in JavaScript.
Bearbeiten : Der Wert von C # wird im JS-Debugger-Fenster nur gekürzt. Wie @Pete erwähnt hat, wird es auf etwas erweitert, das nicht genau 0,5 ist (0,005000000000000000104083408558). Dies bedeutet, dass die Diskrepanz zumindest auf beiden Seiten auftritt.
Die JSON-Serialisierung hat nicht das gleiche Problem, da ich davon ausgehe, dass sie über eine Zeichenfolge erfolgt, wodurch die empfangende Umgebung die Kontrolle über das Parsen des Werts in seinen nativen numerischen Typ behält.
Ich frage mich, ob es eine Möglichkeit gibt, mithilfe der binären Serialisierung auf beiden Seiten übereinstimmende Werte zu erhalten.
Wenn nicht, bedeutet dies, dass es keine Möglichkeit gibt, 100% genaue binäre Konvertierungen zwischen JavaScript und C # durchzuführen?
Verwendete Technologie:
- JavaScript
- .Net Core mit SignalR und msgpack5
Mein Code basiert auf diesem Beitrag . Der einzige Unterschied ist, dass ich benutze ContractlessStandardResolver.Instance
.
Antworten:
AKTUALISIEREN
Dies wurde in der nächsten Version (5.0.0-Vorschau4) behoben .
Ursprüngliche Antwort
Ich habe getestet
float
unddouble
, und interessanterweise in diesem speziellen Fall, nurdouble
das Problem gehabt, währendfloat
es zu funktionieren scheint (dh 0,005 wird auf dem Server gelesen).Die Überprüfung der Nachrichtenbytes ergab, dass 0,005 als Typ gesendet wird, bei
Float32Double
dem es sich um eine 4-Byte / 32-Bit-Gleitkommazahl mit einfacher Genauigkeit nach IEEE 754 handelt, obwohlNumber
es sich um 64-Bit-Gleitkommazahlen handelt.Führen Sie den folgenden Code in der Konsole aus, um Folgendes zu bestätigen:
mspack5 bietet eine Option zum Erzwingen eines 64-Bit-Gleitkommas:
Die
forceFloat64
Option wird jedoch nicht von signalr-protocol-msgpack verwendet .Das erklärt zwar, warum es
float
auf der Serverseite funktioniert, aber es gibt derzeit keine wirkliche Lösung dafür . Warten wir, was Microsoft sagt .Mögliche Problemumgehungen
forceFloat64
Standardwert true ?? Ich weiß es nicht.float
auf die Serverseitestring
auf beiden Seitendecimal
auf die Serverseite und schreiben Sie benutzerdefiniertIFormatterProvider
.decimal
ist kein primitiver Typ undIFormatterProvider<decimal>
wird für komplexe Typeneigenschaften aufgerufendouble
Eigenschaftswerts an und führen Sie den Trickdouble
->float
->decimal
-> ausdouble
TL; DR
Das Problem, dass der JS-Client eine einzelne Gleitkommazahl an das C # -Backend sendet, verursacht ein bekanntes Gleitkommaproblem:
Für die direkte Verwendung von
double
in-Methoden könnte das Problem durch eine benutzerdefinierte Lösung gelöst werdenMessagePack.IFormatterResolver
:Und benutze den Resolver:
Der Resolver ist nicht perfekt, da das Gießen bis
decimal
dahindouble
den Prozess verlangsamt und gefährlich sein kann .jedoch
Gemäß dem in den Kommentaren genannten OP kann dies das Problem nicht lösen, wenn komplexe Typen mit
double
zurückgegebenen Eigenschaften verwendet werden.Weitere Untersuchungen ergaben die Ursache des Problems in MessagePack-CSharp:
Der obige Decoder wird verwendet, wenn eine einzelne
float
Zahl konvertiert werden muss indouble
:v2
Dieses Problem tritt in Version 2 von MessagePack-CSharp auf. Ich habe ein Problem bei Github eingereicht , das Problem wird jedoch nicht behoben .
quelle
float
eine Problemumgehung verwenden. Ich weiß nicht, ob sie das in v2 behoben haben. Ich werde einen Blick darauf werfen, sobald ich etwas Zeit habe. Das Problem ist jedoch, dass v2 noch nicht mit SignalR kompatibel ist. Nur Vorschauversionen (5.0.0.0- *) von SignalR können v2 verwenden.Bitte überprüfen Sie den genauen Wert, den Sie senden, genauer. Sprachen beschränken normalerweise die Genauigkeit des Drucks, damit er besser aussieht.
quelle