C# 使用JsonSerializer序列化double会导致JSON精度较低.net 5.0

C# 使用JsonSerializer序列化double会导致JSON精度较低.net 5.0,c#,formatting,rounding,.net-5,C#,Formatting,Rounding,.net 5,从dotnet core 2.2升级到dotnet 5.0时,我们经历了失败的测试,这些测试依赖于对象的一致序列化来生成哈希。我一直在跟踪double类型在传递给JsonSerializer.Serialize方法时的序列化方式。在序列化过程中,如果一个double被四舍五入到下一个小数点,但不是每个数字都被四舍五入,那么我们似乎会失去精度 比如说 var d = 50.494329039350461; var dAsText = System.Text.Json.JsonSerializer

从dotnet core 2.2升级到dotnet 5.0时,我们经历了失败的测试,这些测试依赖于对象的一致序列化来生成哈希。我一直在跟踪double类型在传递给JsonSerializer.Serialize方法时的序列化方式。在序列化过程中,如果一个double被四舍五入到下一个小数点,但不是每个数字都被四舍五入,那么我们似乎会失去精度

比如说

var d = 50.494329039350461;

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

//Value of dAsText is 50.49432903935046
当我们反序列化时,我们得到了原始的数字,但是我们需要对序列化的数据采取行动来生成我们的散列。为什么这种行为在不同的框架之间发生了变化,这是一个bug还是有意的?我们是否可以更改任何设置以恢复以前的实现(当然,保留在.net 5上)。Newtonsoft Json也有同样的行为(我也向他们提出了一个问题)

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

问题也可以从以下数字中看出:

50.494328391915907 30.316339899700989
50.494128852095287

我认为这是我们的努力的结果。这篇文章有许多值得一读的变化,但特别是:

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
精度有限)。正如我从那篇文章中了解到的,现在返回最短RoundTripable值,
d2
确实是最短的

文章还说,

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

实际上,使用
d.ToString(“G17”)
返回您期望的结果


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

谢谢,我并不假装我完全理解这方面的细节,但您对以前的行为作为错误的描述很有启发性。我想我们从对象的Json生成has的实现不是理想的,因为这两个数字是相同的,当用double表示时,所以它们最好生成相同的散列。