Al serializar y deserializar valores entre JavaScript y C # usando SignalR con MessagePack, veo un poco de pérdida de precisión en C # en el extremo receptor.
Como ejemplo, estoy enviando el valor 0.005 de JavaScript a C #. Cuando el valor deserializado aparece en el lado de C # obtengo el valor 0.004999999888241291
, que está cerca, pero no exactamente 0.005. El valor en el lado de JavaScript es Number
y en el lado de C # que estoy usando double
.
He leído que JavaScript no puede representar números de coma flotante exactamente, lo que puede conducir a resultados como 0.1 + 0.2 == 0.30000000000000004
. Sospecho que el problema que estoy viendo está relacionado con esta característica de JavaScript.
Lo interesante es que no veo el mismo problema al revés. Enviar 0.005 de C # a JavaScript da como resultado el valor 0.005 en JavaScript.
Editar : el valor de C # se acorta en la ventana del depurador JS. Como @Pete mencionó, se expande a algo que no es 0.5 exactamente (0.005000000000000000104083408558). Esto significa que la discrepancia ocurre en ambos lados al menos.
La serialización JSON no tiene el mismo problema, ya que supongo que va a través de una cadena que deja el entorno de recepción en control wrt analizando el valor en su tipo numérico nativo.
Me pregunto si hay una forma de usar la serialización binaria para tener valores coincidentes en ambos lados.
Si no, ¿significa esto que no hay forma de tener conversiones binarias 100% precisas entre JavaScript y C #?
Tecnología utilizada:
- JavaScript
- .Net Core con SignalR y msgpack5
Mi código se basa en esta publicación . La única diferencia es que estoy usando ContractlessStandardResolver.Instance
.
Respuestas:
ACTUALIZAR
Esto se ha solucionado en la próxima versión (5.0.0-preview4) .
Respuesta original
Lo probé
float
ydouble
, curiosamente en este caso particular, solodouble
tuve el problema, mientras quefloat
parece estar funcionando (es decir, 0.005 se lee en el servidor).La inspección en los bytes del mensaje sugirió que 0.005 se envía como tipo,
Float32Double
que es un número de coma flotante de precisión simple IEEE 754 de 4 bytes / 32 bits a pesar deNumber
ser un punto flotante de 64 bits.Ejecute el siguiente código en la consola confirmó lo anterior:
mspack5 proporciona una opción para forzar el punto flotante de 64 bits:
Sin embargo, la
forceFloat64
opción no es utilizada por signalr-protocol-msgpack .Aunque eso explica por qué
float
funciona en el lado del servidor, pero no hay realmente una solución para eso a partir de ahora . Esperemos lo que dice Microsoft .Posibles soluciones
forceFloat64
valor predeterminado verdadero? No lo sé.float
lado del servidorstring
en ambos ladosdecimal
lado del servidor y escriba personalizadoIFormatterProvider
.decimal
no es de tipo primitivo yIFormatterProvider<decimal>
se llama para propiedades de tipo complejodouble
valor de la propiedad y haga el trucodouble
->float
->decimal
->double
TL; DR
El problema con el cliente JS que envía un único número de coma flotante al backend de C # causa un problema conocido de coma flotante:
Para usos directos de los
double
métodos in, el problema podría resolverse mediante una costumbreMessagePack.IFormatterResolver
:Y usa el resolutor:
El resolutor no es perfecto, ya que lanzarlo a
decimal
continuacióndouble
ralentiza el proceso y podría ser peligroso .sin embargo
Según el OP señalado en los comentarios, esto no puede resolver el problema si se utilizan tipos complejos con
double
propiedades de retorno.La investigación adicional reveló la causa del problema en MessagePack-CSharp:
El decodificador anterior se usa cuando se necesita convertir un solo
float
número adouble
:v2
Este problema existe en las versiones v2 de MessagePack-CSharp. He presentado un problema en github , aunque el problema no se solucionará .
fuente
float
como solución alternativa. No sé si lo arreglaron en v2. Echaré un vistazo una vez que tenga algo de tiempo. Sin embargo, el problema es que v2 aún no es compatible con SignalR. Solo las versiones de vista previa (5.0.0.0- *) de SignalR pueden usar v2.Verifique el valor exacto que está enviando con mayor precisión. Los idiomas generalmente limitan la precisión en la impresión para que se vea mejor.
fuente