GÜNCELLEME
Bu bir sonraki sürümde düzeltildi (5.0.0-önizleme4) .
Orijinal Yanıt
Ben test floatve doubleve ilginç bu özel durumda, sadece double, oysa sorun vardı float(yani 0.005 sunucuda okunur) çalışıyor gibi görünüyor.
İleti baytları üzerinde inceleme, 0.005'in 64 bit kayan nokta Float32Doubleolmasına rağmen 4 bayt / 32 bit IEEE 754 tek hassas kayan nokta sayısı olan tip olarak gönderilmesini önerdi Number.
Yukarıda doğrulanan konsolda aşağıdaki kodu çalıştırın:
msgpack5().encode(Number(0.005))
// Output
Uint8Array(5) [202, 59, 163, 215, 10]
mspack5 , 64 bit kayan noktayı zorlamak için bir seçenek sunar:
msgpack5({forceFloat64:true}).encode(Number(0.005))
// Output
Uint8Array(9) [203, 63, 116, 122, 225, 71, 174, 20, 123]
Ancak, bu forceFloat64seçenek signalr-protocol-msgpack tarafından kullanılmaz .
Bu neden floatsunucu tarafında çalıştığını açıklasa da, şu an için bunun için bir düzeltme yok . Microsoft'un söylediklerini bekleyelim .
Olası çözümler
- Msgpack5 seçenekleri kesmek? Çatal ve kendi msgpack5
forceFloat64varsayılan doğru ile derlemek ?? Bilmiyorum.
- Geçin
float, sunucu tarafında
stringHer iki tarafta da kullanın
decimalSunucu tarafına geçin ve özel yazın IFormatterProvider. decimalilkel tür değildir ve IFormatterProvider<decimal>karmaşık tür özellikleri için çağrılır
doubleÖzellik değerini almak için yöntem sağlayın ve double-> float-> decimal-> doublehile yapın
- Aklınıza gelebilecek diğer gerçekçi olmayan çözümler
TL; DR
JS istemcisinin C # arka ucuna tek kayan nokta sayısı gönderme sorunu bilinen bir kayan nokta sorununa neden olur:
// value = 0.00499999988824129, crazy C# :)
var value = (double)0.005f;
doubleYöntemlerde doğrudan kullanım için , sorun bir gelenek tarafından çözülebilir 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;
}
}
Ve çözümleyiciyi kullanın:
services.AddSignalR()
.AddMessagePackProtocol(options =>
{
options.FormatterResolvers = new List<MessagePack.IFormatterResolver>()
{
MyDoubleFormatterResolver.Instance,
ContractlessStandardResolver.Instance,
};
});
Çözümleyici mükemmel değildir, çünkü decimalo zaman doublesüreci yavaşlatmak için döküm yapmak ve tehlikeli olabilir .
ancak
Açıklamalarda belirttiği OP gereğince, bu olamaz sahip karmaşık türleri kullanarak eğer sorunu çözmek doubledönen özelliklerini.
Daha fazla araştırma, MessagePack-CSharp'ta sorunun nedenini ortaya çıkardı:
// 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;
}
}
}
Yukarıdaki kod çözücü, tek bir floatsayıyı şuna dönüştürmek gerektiğinde kullanılır double:
// From MessagePackBinary class
MessagePackBinary.doubleDecoders[202] = Float32Double.Instance;
v2
Bu sorun, MessagePack-CSharp'ın v2 sürümlerinde bulunmaktadır. Ben bulundular github üzerinde bir sorunu , sorunun düzeltilmesi için gittiğini olmasa da .