UPDATE
นี้ได้รับการแก้ไขในรุ่นถัดไป (5.0.0-preview4)
คำตอบเดิม
ฉันทดสอบfloat
และdouble
และน่าสนใจในกรณีนี้โดยเฉพาะdouble
มีปัญหาในขณะที่float
ดูเหมือนว่าจะทำงาน (เช่น 0.005 อ่านบนเซิร์ฟเวอร์)
การตรวจสอบไบต์ของข้อความแนะนำว่า 0.005 ถูกส่งเป็นชนิดFloat32Double
ซึ่งเป็นเลขทศนิยม 4 จุดความแม่นยำเดี่ยว IEEE 754 4 ไบต์ / 32- บิตแม้จะ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]
อย่างไรก็ตามforceFloat64
ตัวเลือกที่จะไม่ใช้signalr โปรโตคอล msgpack
แม้ว่าที่อธิบายว่าทำไมfloat
ทำงานบนฝั่งเซิร์ฟเวอร์แต่มีไม่ได้จริงๆแก้ไขสำหรับว่า ณ ตอนนี้ รอ Let 's สิ่งที่ไมโครซอฟท์กล่าวว่า
วิธีแก้ไขที่เป็นไปได้
- แฮก msgpack5 ตัวเลือก? แยกและรวบรวม msgpack5 ของคุณเองด้วย
forceFloat64
ค่าเริ่มต้นเป็นจริง ฉันไม่รู้
- สลับไป
float
ที่ฝั่งเซิร์ฟเวอร์
- ใช้
string
ทั้งสองด้าน
- สลับไปในฝั่งเซิร์ฟเวอร์และการเขียนที่กำหนดเอง
decimal
ไม่ใช่ชนิดดั้งเดิมและถูกเรียกใช้สำหรับคุณสมบัติชนิดที่ซับซ้อนIFormatterProvider
decimal
IFormatterProvider<decimal>
- จัดเตรียมวิธีในการเรียกคืน
double
ค่าคุณสมบัติและทำเคล็ดลับdouble
-> float
-> decimal
->double
- โซลูชันที่ไม่สมจริงอื่น ๆ ที่คุณนึกออก
TL; DR
ปัญหาเกี่ยวกับไคลเอ็นต์ JS ที่ส่งหมายเลขจุดลอยตัวเดียวไปยังแบ็กเอนด์ C # ทำให้เกิดปัญหาการรู้จุดลอยตัว:
// 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
กระบวนการช้าลงและอาจเป็นอันตรายได้
อย่างไรก็ตาม
ตาม 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 , แม้ว่าปัญหาจะไม่ได้รับการแก้ไข