【问题标题】:Serializing a double with JsonSerializer results in JSON with lower precision .net 5.0使用 JsonSerializer 序列化双精度会导致 JSON 精度较低 .net 5.0
【发布时间】:2020-11-26 14:30:36
【问题描述】:

在从 dotnet core 2.2 升级到 dotnet 5.0 时,我们遇到了失败的测试,这些测试依赖于对象的一致序列化来生成哈希。我已经将此追踪到当传递给 JsonSerializer.Serialize 方法时双精度类型被序列化的方式。在序列化过程中,我们似乎会丢失精度,其中双精度数舍入到下一个小数点,但并非每个数字都被舍入。

例如

var d = 50.494329039350461;

var dAsText = System.Text.Json.JsonSerializer.Serialize(d);

//Value of dAsText is 50.49432903935046

当我们反序列化时,我们会取回原始数字,但我们需要对序列化数据进行操作以生成我们的哈希。为什么这种行为在框架之间发生了变化,是错误还是有意为之?是否有任何设置我们可以更改以恢复以前的实现(当然保留在 .net 5 上)。使用 Newtonsoft Json 可以看到相同的行为(我也向他们提出了问题)

在 Windows 10x64 和 Lunix Docker 映像中的行为似乎一致。这是在 Visual Studio 2019 中运行的(最新更新)

这些数字也可以看出问题:

50.494328391915907 30.316339899700989 50.494128852095287

【问题讨论】:

    标签: c# formatting rounding .net-5


    【解决方案1】:

    我认为这是Floating-Point Parsing and Formatting improvements in .NET Core 3.0 的结果。有很多变化值得一读这篇文章,但特别是:

    ToString()、ToString("G") 和 ToString("R") 现在将返回 最短往返字符串

    不确定 System.Text.Json,但 Newtonsoft Json 在编写 double 时使用“R”说明符(这是有道理的)。使用d.ToString("R") 会重现此问题。

    请注意,您的号码和它序列化的号码实际上是相等的:

    var d =  50.494329039350461d;
    var d2 = 50.49432903935046d;
    var sameThing = d == d2; // true
    bool sameBytes = BitConverter.GetBytes(d).SequenceEqual(BitConverter.GetBytes(d2)); // true
    

    所以最后一个“1”数字基本上是没有意义的(由于double精度有限)。正如我从那篇文章中了解到的那样 - 现在返回了最短的可往返值,d2 确实是最短的。

    文章还说:

    对于 ToString("R"),没有回退到旧的机制 行为。以前的行为会首先尝试“G15”,然后使用 内部缓冲区会查看它是否往返;如果失败了,它 而是返回“G17”。

    确实使用d.ToString("G17") 会返回您期望的结果。

    因此,这是预期行为(以前的行为被认为是“错误”并已修复),据我所知 - 没有设置可以恢复到以前的行为。

    【讨论】:

    • 谢谢你,我并没有假装我完全理解这个细节,但是你对以前行为作为一个错误的描述是有启发性的。我猜我们从对象的 Json 生成 has 的实现并不理想
    • 我猜是这样,因为这两个数字是相同的,当用双精度表示时,它们最好产生相同的哈希值。
    猜你喜欢
    • 1970-01-01
    • 2023-04-05
    • 2018-11-14
    • 1970-01-01
    • 2013-06-17
    • 1970-01-01
    • 2012-11-07
    • 2018-06-05
    • 1970-01-01
    相关资源
    最近更新 更多