Lors de la sérialisation et de la désérialisation des valeurs entre JavaScript et C # à l'aide de SignalR avec MessagePack, je constate un peu de perte de précision en C # à la réception.
Par exemple, j'envoie la valeur 0,005 de JavaScript à C #. Lorsque la valeur désérialisée apparaît du côté C #, j'obtiens la valeur 0.004999999888241291
, qui est proche, mais pas 0,005 exactement. La valeur du côté JavaScript est Number
et du côté C # que j'utilise double
.
J'ai lu que JavaScript ne peut pas représenter exactement des nombres à virgule flottante, ce qui peut conduire à des résultats comme 0.1 + 0.2 == 0.30000000000000004
. Je soupçonne que le problème que je vois est lié à cette fonctionnalité de JavaScript.
La partie intéressante est que je ne vois pas le même problème aller dans l'autre sens. L'envoi de 0,005 de C # vers JavaScript entraîne la valeur 0,005 en JavaScript.
Edit : La valeur de C # est juste raccourcie dans la fenêtre du débogueur JS. Comme @Pete l'a mentionné, il s'étend à quelque chose qui n'est pas exactement 0,5 (0,005000000000000000104083408558). Cela signifie que l'écart se produit au moins des deux côtés.
La sérialisation JSON n'a pas le même problème car je suppose qu'elle passe par une chaîne qui laisse l'environnement récepteur sous contrôle en analysant la valeur dans son type numérique natif.
Je me demande s'il existe un moyen d'utiliser la sérialisation binaire pour avoir des valeurs correspondantes des deux côtés.
Sinon, cela signifie-t-il qu'il n'y a aucun moyen d'avoir des conversions binaires 100% précises entre JavaScript et C #?
Technologie utilisée:
- Javascript
- .Net Core avec SignalR et msgpack5
Mon code est basé sur ce post . La seule différence est que j'utilise ContractlessStandardResolver.Instance
.
Réponses:
MISE À JOUR
Ce problème a été corrigé dans la prochaine version (5.0.0-preview4) .
Réponse originale
J'ai testé
float
etdouble
, et c'est intéressant dans ce cas particulier, je n'aidouble
eu que le problème, alors qu'ilfloat
semble fonctionner (c'est-à-dire que 0,005 est lu sur le serveur).L'inspection des octets de message a suggéré que 0,005 est envoyé en tant que type
Float32Double
qui est un nombre à virgule flottante à simple précision IEEE 754 de 4 octets / 32 bits bien qu'ilNumber
soit de 64 bits à virgule flottante.Exécutez le code suivant dans la console a confirmé ce qui précède:
mspack5 fournit une option pour forcer la virgule flottante 64 bits:
Cependant, l'
forceFloat64
option n'est pas utilisée par signalr-protocol-msgpack .Bien que cela explique pourquoi
float
fonctionne côté serveur, mais il n'y a pas vraiment de correctif pour le moment . Attendons ce que dit Microsoft .Solutions de contournement possibles
forceFloat64
valeur par défaut true? Je ne sais pas.float
côté serveurstring
deux côtésdecimal
côté serveur et écrivez personnaliséIFormatterProvider
.decimal
n'est pas de type primitif etIFormatterProvider<decimal>
est appelé pour des propriétés de type complexesdouble
valeur de la propriété et faire l' astucedouble
->float
->decimal
->double
TL; DR
Le problème avec le client JS envoyant un seul nombre à virgule flottante au backend C # provoque un problème de virgule flottante connu:
Pour les utilisations directes des
double
méthodes in, le problème peut être résolu par une personnalisationMessagePack.IFormatterResolver
:Et utilisez le résolveur:
Le résolveur n'est pas parfait, car couler jusque-
decimal
là pourdouble
ralentir le processus et cela pourrait être dangereux .toutefois
Comme l'OP l'a souligné dans les commentaires, cela ne peut pas résoudre le problème si vous utilisez des types complexes ayant des
double
propriétés de retour.Une enquête plus approfondie a révélé la cause du problème dans MessagePack-CSharp:
Le décodeur ci-dessus est utilisé pour convertir un seul
float
nombre endouble
:v2
Ce problème existe dans les versions v2 de MessagePack-CSharp. J'ai déposé un problème sur github , bien que le problème ne soit pas résolu .
la source
float
cette solution. Je ne sais pas s'ils ont corrigé cela en v2. Je jetterai un coup d'œil une fois que j'aurai du temps. Cependant, le problème est que la v2 n'est pas encore compatible avec SignalR. Seules les versions d'aperçu (5.0.0.0- *) de SignalR peuvent utiliser la v2.Veuillez vérifier la valeur précise que vous envoyez avec une plus grande précision. Les langues limitent généralement la précision de l'impression pour la rendre plus belle.
la source