Ao serializar e desserializar valores entre JavaScript e C # usando o SignalR com MessagePack, estou vendo um pouco de perda de precisão em C # no lado de recebimento.
Como exemplo, estou enviando o valor 0,005 do JavaScript para o C #. Quando o valor desserializado aparece no lado do C #, estou recebendo o valor 0.004999999888241291
, que está próximo, mas não exatamente 0,005. O valor no lado JavaScript é Number
e no lado C # que estou usando double
.
Eu li que o JavaScript não pode representar números de ponto flutuante exatamente o que pode levar a resultados como 0.1 + 0.2 == 0.30000000000000004
. Suspeito que o problema que estou vendo esteja relacionado a esse recurso do JavaScript.
A parte interessante é que não estou vendo o mesmo problema no outro sentido. Enviar 0,005 de C # para JavaScript resulta no valor 0,005 em JavaScript.
Edit : O valor de C # é apenas reduzido na janela do depurador JS. Como o @Pete mencionou, ele se expande para algo que não é exatamente 0,5 (0,005000000000000000104083408558). Isso significa que a discrepância acontece nos dois lados, pelo menos.
A serialização JSON não tem o mesmo problema, pois estou assumindo que ela passa por uma string que deixa o ambiente de recebimento no controle wrt, analisando o valor em seu tipo numérico nativo.
Gostaria de saber se existe uma maneira de usar a serialização binária para ter valores correspondentes em ambos os lados.
Caso contrário, isso significa que não há como ter conversões binárias 100% precisas entre JavaScript e C #?
Tecnologia utilizada:
- Javascript
- .Net Core com SignalR e msgpack5
Meu código é baseado nesta postagem . A única diferença é que eu estou usando ContractlessStandardResolver.Instance
.
Respostas:
ATUALIZAR
Isso foi corrigido na próxima versão (5.0.0-preview4) .
Resposta original
Eu testei
float
edouble
, e , curiosamente, neste caso em particular, sódouble
tive o problema, enquantofloat
parece estar funcionando (ou seja, 0,005 é lido no servidor).A inspeção nos bytes da mensagem sugeriu que 0,005 é enviado como um tipo de
Float32Double
ponto flutuante de precisão única de 4 bytes / 32 bits IEEE 754, apesar deNumber
ser um ponto flutuante de 64 bits.Execute o seguinte código no console confirmou o acima:
O mspack5 fornece uma opção para forçar o ponto flutuante de 64 bits:
No entanto, a
forceFloat64
opção não é usada pelo signalr-protocol-msgpack .Embora isso explique por que
float
funciona no lado do servidor, mas não há realmente uma correção para isso a partir de agora . Vamos esperar o que a Microsoft diz .Soluções possíveis
forceFloat64
padrão true? Eu não sei.float
lado do servidorstring
nos dois ladosdecimal
lado do servidor e escreva personalizadoIFormatterProvider
.decimal
não é do tipo primitivo eIFormatterProvider<decimal>
é chamado para propriedades de tipo complexasdouble
valor da propriedade e faça o truquedouble
->float
->decimal
->double
TL; DR
O problema com o cliente JS enviando um número de ponto flutuante único para o back-end em C # causa um problema conhecido de ponto flutuante:
Para usos diretos de
double
métodos, o problema pode ser resolvido por um costumeMessagePack.IFormatterResolver
:E use o resolvedor:
O resolvedor não é perfeito, pois a conversão para
decimal
entãodouble
atrasa o processo e pode ser perigoso .Contudo
Conforme o OP apontado nos comentários, isso não pode resolver o problema se estiver usando tipos complexos com
double
propriedades retornadas.Uma investigação mais aprofundada revelou a causa do problema no MessagePack-CSharp:
O decodificador acima é usado quando é necessário converter um único
float
número paradouble
:v2
Esse problema existe nas versões v2 do MessagePack-CSharp. Eu arquivei um problema no github , embora o problema não seja corrigido .
fonte
float
como solução alternativa. Não sei se eles corrigiram isso na v2. Vou dar uma olhada assim que tiver algum tempo. No entanto, o problema é que a v2 ainda não é compatível com o SignalR. Somente versões de visualização (5.0.0.0- *) do SignalR podem usar a v2.Verifique o valor exato que você está enviando para obter uma precisão maior. Os idiomas normalmente limitam a precisão na impressão para torná-la melhor.
fonte