更新
これは次のリリース(5.0.0-preview4)で修正されました。
元の回答
私はをテストfloatしdouble、そして興味深いことに、この特定のケースでdoubleは、問題があるだけでしたが、機能しているfloatようです(つまり、サーバーで0.005が読み取られます)。
メッセージバイトを調べると、64ビットの浮動小数点であるFloat32Doubleにもかかわらず、0.005が4バイト/ 32ビットのIEEE 754単精度浮動小数点数であるタイプとして送信されることが示唆されましたNumber。
上記で確認されたコンソールで次のコードを実行します。
msgpack5().encode(Number(0.005))
// Output
Uint8Array(5) [202, 59, 163, 215, 10]
mspack5には、64ビット浮動小数点を強制するオプションが用意されています。
msgpack5({forceFloat64:true}).encode(Number(0.005))
// Output
Uint8Array(9) [203, 63, 116, 122, 225, 71, 174, 20, 123]
ただし、このforceFloat64オプションはsignalr-protocol-msgpackでは使用されません。
それがfloatサーバー側で機能する理由を説明していますが、現時点では実際には修正されていません。マイクロソフトの発言を待ちましょう。
可能な回避策
- msgpack5オプションをハックしますか?独自のmsgpack5をforkし、
forceFloat64デフォルトでtrueにコンパイルしますか?知りません。
floatサーバー側に切り替え
string両面使用
decimalサーバー側に切り替え、カスタムを記述しますIFormatterProvider。decimalプリミティブ型ではなくIFormatterProvider<decimal>、複合型プロパティのために呼び出されます
doubleプロパティ値を取得してdouble-> float-> decimal-> doubleトリックを行うメソッドを提供する
- あなたが考えることができる他の非現実的な解決策
TL; DR
単一の浮動小数点数をC#バックエンドに送信するJSクライアントの問題により、既知の浮動小数点の問題が発生します。
// value = 0.00499999988824129, crazy C# :)
var value = (double)0.005f;
doubleinメソッドを直接使用する場合、問題はカスタムで解決できますMessagePack.IFormatterResolver。
public class MyDoubleFormatterResolver : IFormatterResolver
{
public static MyDoubleFormatterResolver Instance = new MyDoubleFormatterResolver();
private MyDoubleFormatterResolver()
{ }
public IMessagePackFormatter<T> GetFormatter<T>()
{
return MyDoubleFormatter.Instance as IMessagePackFormatter<T>;
}
}
public sealed class MyDoubleFormatter : IMessagePackFormatter<double>, IMessagePackFormatter
{
public static readonly MyDoubleFormatter Instance = new MyDoubleFormatter();
private MyDoubleFormatter()
{
}
public int Serialize(
ref byte[] bytes,
int offset,
double value,
IFormatterResolver formatterResolver)
{
return MessagePackBinary.WriteDouble(ref bytes, offset, value);
}
public double Deserialize(
byte[] bytes,
int offset,
IFormatterResolver formatterResolver,
out int readSize)
{
double value;
if (bytes[offset] == 0xca)
{
// 4 bytes single
// cast to decimal then double will fix precision issue
value = (double)(decimal)MessagePackBinary.ReadSingle(bytes, offset, out readSize);
return value;
}
value = MessagePackBinary.ReadDouble(bytes, offset, out readSize);
return value;
}
}
そしてリゾルバを使用します:
services.AddSignalR()
.AddMessagePackProtocol(options =>
{
options.FormatterResolvers = new List<MessagePack.IFormatterResolver>()
{
MyDoubleFormatterResolver.Instance,
ContractlessStandardResolver.Instance,
};
});
キャストとしてリゾルバは、完璧ではないdecimalにその後、doubleスローダウンプロセスをし、それは危険なことができ。
しかしながら
コメントで指摘されたOPのとおり、プロパティを返す複合型を使用する場合、これは問題を解決できませんdouble。
さらなる調査により、MessagePack-CSharpの問題の原因が判明しました。
// Type: MessagePack.MessagePackBinary
// Assembly: MessagePack, Version=1.9.0.0, Culture=neutral, PublicKeyToken=b4a0369545f0a1be
// MVID: B72E7BA0-FA95-4EB9-9083-858959938BCE
// Assembly location: ...\.nuget\packages\messagepack\1.9.11\lib\netstandard2.0\MessagePack.dll
namespace MessagePack.Decoders
{
internal sealed class Float32Double : IDoubleDecoder
{
internal static readonly IDoubleDecoder Instance = (IDoubleDecoder) new Float32Double();
private Float32Double()
{
}
public double Read(byte[] bytes, int offset, out int readSize)
{
readSize = 5;
// The problem is here
// Cast a float value to double like this causes precision loss
return (double) new Float32Bits(bytes, checked (offset + 1)).Value;
}
}
}
上記のデコーダは、単一のfloat数値をに変換する必要がある場合に使用されdoubleます。
// From MessagePackBinary class
MessagePackBinary.doubleDecoders[202] = Float32Double.Instance;
v2
この問題は、MessagePack-CSharpのv2バージョンに存在します。私はgithubに問題を提出しましたが、問題は修正されません。