최신 정보
이것은 다음 릴리스 (5.0.0-preview4)에서 수정 되었습니다 .
원래 답변
나는 이 특별한 경우에 테스트 float
하고 double
흥미롭게 만 double
문제가있는 반면 float
작동 하는 것처럼 보입니다 (즉, 서버에서 0.005를 읽습니다).
메시지 바이트를 조사한 결과, 0.005 가 64 비트 부동 소수점 Float32Double
임에도 불구하고 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]
그러나 signalr-protocol-msgpack은이forceFloat64
옵션을 사용하지 않습니다 .
그것은 왜 float
서버 측에서 작동 하는지 설명 하지만, 현재로서는 그에 대한 해결책이 없습니다 . 마이크로 소프트의 말을 기다리 자 .
가능한 해결 방법
- msgpack5 옵션을 해킹 하시겠습니까? 포크로 자신의 msgpack5를
forceFloat64
기본값으로 true로 컴파일하십시오 ?? 모르겠어요
float
서버 측으로 전환
- 사용하여
string
양쪽에
decimal
서버 측으로 전환하고 custom을 작성하십시오 IFormatterProvider
. decimal
기본 유형이 아니며 IFormatterProvider<decimal>
복합 유형 특성에 대해 호출됩니다.
double
속성 값 을 검색 하고 double
-> float
-> decimal
-> double
트릭을 수행하는 방법을 제공하십시오.
- 생각할 수있는 다른 비현실적인 솔루션
TL; DR
JS 클라이언트가 단일 부동 소수점 숫자를 C # 백엔드로 보내는 문제로 인해 알려진 부동 소수점 문제가 발생합니다.
// value = 0.00499999988824129, crazy C# :)
var value = (double)0.005f;
double
in 메소드 를 직접 사용 하는 경우 사용자 지정으로 문제를 해결할 수 있습니다 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
이 문제는 v2 버전의 MessagePack-CSharp에 존재합니다. 내가 제기 한 GitHub의에 문제를 , 문제가 해결 될 것 아니지만 .