अपडेट करें
यह अगली रिलीज में तय किया गया है (5.0.0-प्रीव्यू 4) ।
मूल उत्तर
मैंने इस विशेष मामले में float
और double
, और दिलचस्प रूप से परीक्षण किया , केवल double
समस्या थी, जबकिfloat
काम करने (यानी 0.005 सर्वर पर पढ़ा जाता है) लगता है।
संदेश बाइट्स पर निरीक्षण ने सुझाव दिया कि 0.005 को टाइप के रूप में भेजा जाता है Float32Double
जो 4-बाइट / 32-बिट IEEE 754 एकल परिशुद्धता फ़्लोटिंग पॉइंट नंबर के बावजूद Number
64 बिट फ़्लोटिंग पॉइंट है।
कंसोल में निम्न कोड चलाएँ उपरोक्त पुष्टि की:
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]
हालाँकि, सिग्नल-प्रोटोकॉल-msgpackforceFloat64
द्वारा विकल्प का उपयोग नहीं किया जाता है ।
हालांकि यह बताता है कि float
सर्वर की तरफ काम क्यों होता है , लेकिन वास्तव में इसके लिए कोई फिक्स नहीं है । Microsoft क्या कहता है प्रतीक्षा करें ।
संभावित वर्कअराउंड
- Mspack5 विकल्प हैक करें? कांटा और अपना खुद का msgpack5 संकलित करें
forceFloat64
डिफ़ॉल्ट के सच ?? मुझे नहीं पता।
- पर स्विच
float
सर्वर साइड पर
- उपयोग
string
दोनों तरफ का
- पर स्विच
decimal
सर्वर साइड और लिखने कस्टम पर IFormatterProvider
। decimal
आदिम प्रकार नहीं है, और IFormatterProvider<decimal>
जटिल प्रकार के गुणों के लिए कहा जाता है
double
संपत्ति मूल्य प्राप्त करने के लिए विधि प्रदान करें और double
-> float
-> करेंdecimal
-> double
चाल करें
- अन्य अवास्तविक समाधान आप सोच सकते हैं
टी एल; डॉ
जेएस क्लाइंट के साथ सी # बैकएंड पर सिंगल फ्लोटिंग पॉइंट नंबर भेजने की समस्या एक फ्लोटिंग पॉइंट इश्यू का कारण बनती है:
// value = 0.00499999988824129, crazy C# :)
var value = (double)0.005f;
double
तरीकों के प्रत्यक्ष उपयोग के लिए, समस्या को एक कस्टम द्वारा हल किया जा सकता है 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
प्रक्रिया को धीमा करने के लिए कास्टिंग करना और यह खतरनाक हो सकता है ।
तथापि
जैसा कि ओपी ने टिप्पणियों में बताया, यह जटिल प्रकारों का उपयोग करते हुए समस्या को हल नहीं कर सकता है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;
वी 2
यह समस्या MessagePack-CSharp के v2 संस्करणों में मौजूद है। मैंने गितुब पर एक मुद्दा दायर किया है , हालांकि यह मुद्दा तय नहीं होने जा रहा है ।