При сериализации и десериализации значений между JavaScript и C # с использованием SignalR с MessagePack я вижу небольшую потерю точности в C # на принимающей стороне.
В качестве примера я посылаю значение 0,005 из JavaScript в C #. Когда десериализованное значение появляется на стороне C #, я получаю значение 0.004999999888241291
, которое близко, но не точно 0,005. Значение на стороне JavaScript есть Number
и на стороне C #, которую я использую double
.
Я читал, что JavaScript не может точно представлять числа с плавающей точкой, что может привести к результатам, как 0.1 + 0.2 == 0.30000000000000004
. Я подозреваю, что проблема, с которой я сталкиваюсь, связана с этой функцией JavaScript.
Интересно то, что я не вижу другой проблемы, идущей в другую сторону. Отправка 0,005 из C # в JavaScript приводит к значению 0,005 в JavaScript.
Редактировать : значение из C # просто сокращается в окне отладчика JS. Как упомянул @Pete, он расширяется до чего-то, что точно не равно 0.5 (0.005000000000000000104083408558). Это означает, что расхождение происходит по крайней мере с обеих сторон.
Сериализация JSON не имеет той же проблемы, так как я предполагаю, что она проходит через строку, которая оставляет принимающую среду под контролем, анализируя значение в его собственном числовом типе.
Мне интересно, есть ли способ использовать двоичную сериализацию, чтобы иметь совпадающие значения с обеих сторон.
Если нет, значит ли это, что нет никакого способа получить 100% точные двоичные преобразования между JavaScript и C #?
Используемая технология:
- JavaScript
- .Net Core с SignalR и msgpack5
Мой код основан на этом посте . Единственная разница в том, что я использую ContractlessStandardResolver.Instance
.
Ответы:
ОБНОВИТЬ
Это было исправлено в следующем выпуске (5.0.0-preview4) .
Оригинальный ответ
Я проверил,
float
иdouble
, что интересно, в данном конкретном случае толькоdouble
имел проблему только тогда, когда,float
кажется, работает (т. Е. На сервере читается 0,005).Изучение байтов сообщения показало, что 0,005 отправляется как тип
Float32Double
который представляет собой 4-байтовое / 32-битное число с плавающей запятой одинарной точности IEEE 754, несмотря на то, что оноNumber
равно 64-битной.Запустите следующий код в консоли, подтвердив вышесказанное:
mspack5 предоставляет возможность принудительного использования 64-битной плавающей запятой:
Однако
forceFloat64
опция не используется в signalr-protocol-msgpack .Хотя это и объясняет, почему
float
работает на стороне сервера, но на самом деле пока нет исправления . Давайте подождем, что скажет Microsoft .Возможные обходные пути
forceFloat64
значением по умолчанию true? Я не знаю.float
на стороне сервераstring
с обеих сторонdecimal
на серверную часть и напишите на заказIFormatterProvider
.decimal
не примитивный тип, аIFormatterProvider<decimal>
вызывается для свойств сложного типаdouble
значения свойства и выполнитеdouble
->float
->decimal
double
трюк ->TL; DR
Проблема с отправкой JS-клиентом одного числа с плавающей запятой в бэкэнд C # вызывает известную проблему с плавающей запятой:
Для прямого использования
double
методов in, проблема может быть решена с помощью обычаяMessagePack.IFormatterResolver
:И используйте преобразователь:
Распознаватель не является совершенным, так как приведение к
decimal
затемdouble
замедляет процесс вниз и это может быть опасно .Однако
Как указано в комментариях к OP, это не может решить проблему, если использовать сложные типы, имеющие
double
возвращаемые свойства.Дальнейшее расследование выявило причину проблемы в MessagePack-CSharp:
Вышеуказанный декодер используется для преобразования одного
float
числа вdouble
:v2
Эта проблема существует в версии v2 MessagePack-CSharp. Я подал проблему на GitHub , хотя проблема не будет решена .
источник
float
в качестве обходного пути. Я не знаю, исправили ли они это в v2. Я посмотрю, как только у меня будет время. Однако проблема в том, что v2 еще не совместим с SignalR. Только предварительные версии (5.0.0.0- *) SignalR могут использовать v2.Пожалуйста, проверьте точное значение, которое вы отправляете, с большей точностью. Языки обычно ограничивают точность печати, чтобы она выглядела лучше.
источник