gpt4 book ai didi

c# - 在不更改目标类型的情况下以最高精度序列化 float

转载 作者:行者123 更新时间:2023-11-30 16:40:14 26 4
gpt4 key购买 nike

我需要反序列化原始二进制数据 (BinaryFormatter),然后序列化为 JSON(用于编辑),然后再次将其序列化回二进制。显然,我在花车上输了。当我从原始二进制(正确数据和中间数据在内存中为 1:1)序列化为 JSON。我知道这不是什么大损失,我知道 float 有问题,但由于以后可能出现的不兼容问题,我想尽可能保持精度。

有人用 JSON 解决过这个问题吗?如何强制它以最大精度写入 float ?我已经尝试将 float 处理为十进制或 double 的内置选项,但不幸的是,输出没有区别,而且我无法更改目标值,因为当我进行二进制序列化时,它们仍然需要写为 float ,所以会有舍入无论在隐式转换期间。

我尝试往返的包含 float 的特定类型是来自 https://github.com/FNA-XNA/FNA/blob/master/src/Vector2.csVector2 .

tl:dr 有一个 float ,希望 JsonNET 尽可能精确地将它序列化为最终的 json 字符串。

附言我在这里阅读了大量问题,在其他地方阅读了博客条目,但没有发现任何人试图解决同样的问题,大多数搜索结果都与 float 阅读问题有关(我稍后也需要解决)。

更新:正如下面的 @dbc 指出的那样 - Jsont.NET 尊重“TypeConverter”属性,因此我必须制作自己的转换器来覆盖它。

最佳答案

Json.NET 将使用往返精度格式 "R" ( source ) 序列化 float 值。但是,您使用的类型 https://github.com/FNA-XNA/FNA/blob/master/src/Vector2.cs ,应用了 TypeConverter:

    [Serializable]
[TypeConverter(typeof(Vector2Converter))]
[DebuggerDisplay("{DebugDisplayString,nq}")]
public struct Vector2 : IEquatable<Vector2>
{
//...

Newtonsoft docs 中所述,此类类型将使用转换器序列化为字符串:

Primitive Types

.Net: TypeConverter (convertible to String)
JSON: String

然后,检查 https://github.com/FNA-XNA/FNA/blob/master/src/Design/Vector2Converter.cs 的代码,看起来这个转换器在转换为字符串时使用往返精度格式,在around line 60 :

    return string.Join(
culture.TextInfo.ListSeparator,
new string[]
{
vec.X.ToString(culture),
vec.Y.ToString(culture)
}
);

因此内置的 TypeConverter 本身就是您失去精度的地方。

为了避免这个问题,你可以

  1. 创建 custom JsonConverter对于 Vector2,如下所示:

    public class Vector2Converter : JsonConverter
    {
    class Vector2DTO
    {
    public float X;
    public float Y;
    }

    public override bool CanConvert(Type objectType)
    {
    return objectType == typeof(Vector2) || objectType == typeof(Vector2?);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
    // A JSON object is an unordered set of name/value pairs so the converter should handle
    // the X and Y properties in any order.
    var dto = serializer.Deserialize<Vector2DTO>(reader);
    if (dto == null)
    return null;
    return new Vector2(dto.X, dto.Y);
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
    var vec = (Vector2)value;
    serializer.Serialize(writer, new Vector2DTO { X = vec.X, Y = vec.Y });
    }
    }

    如果将转换器添加到 JsonSerializerSettings.Converters它将取代 TypeConverter

    工作样本 fiddle here .

    转换器将 Vector2 序列化为具有 XY 属性的对象,但您也可以将其序列化为具有两个值的数组如果你更喜欢。我不建议将序列化为原语 string,因为 Vector2 不对应于 JSON 原语。

  2. 使用 custom contract resolver它不会为应用了 TypeConverter 的类型创建原始契约,例如 Newtonsoft.JSON cannot convert model with TypeConverter attribute 的答案中显示的那个。 .

注意事项:

  • 往返格式 "R" 显然不保留 +0.0-0.0 之间的差异,因此Json.NET 也没有,请参阅 https://dotnetfiddle.net/aereJ2https://dotnetfiddle.net/DwoGyX一个演示。 Microsoft docs没有提及使用这种格式时是否应保留零号。

    因此,在这种情况下,往返 float 可能会导致二进制更改。

    (感谢 @chuxcomments 中提出此问题。)

  • 顺便说一句,Json.NET 在编写 double 时也使用 "R"(如 source for JsonConvert.ToString(double value, ...) 所示):

    internal static string ToString(double value, FloatFormatHandling floatFormatHandling, char quoteChar, bool nullable)
    {
    return EnsureFloatFormat(value, EnsureDecimalPlace(value, value.ToString("R", CultureInfo.InvariantCulture)), floatFormatHandling, quoteChar, nullable);
    }

    对于double only,这种格式被记录为可能会丢失精度。来自 The Round-trip ("R") Format Specifier :

    In some cases, Double values formatted with the "R" standard numeric format string do not successfully round-trip if compiled using the /platform:x64 or /platform:anycpu switches and run on 64-bit systems.

    因此,通过 Json.NET 往返 double 可能会导致较小的二进制差异。但是,这并不严格适用于这里的问题,它专门针对 float

关于c# - 在不更改目标类型的情况下以最高精度序列化 float ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51703481/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com