ToString在.NET 462和.NET Core 3.1之间具有不同的行为



我正在将一个应用程序从.NET Framework 4.6.2迁移到.NET Core 3.1,我的单元测试在一个我意想不到的地方失败了。

经过一些计算,我得到了一个16位的double。根据调试器,无论是运行.NET462还是.NETCore31代码,我都得到了完全相同的值。当I";串行化";此值。在.NETCore31版本中,最后一位数字丢失:

这里有一个例子:

.NET

4.0584789241077042.ToString("R", CultureInfo.InvariantCulture)
// "4.0584789241077042" (the exact same number)

.NET核心

4.0584789241077042.ToString("R", CultureInfo.InvariantCulture);
// "4.058478924107704" (the last digit is gone)

这实际上不是一个问题,我的计算不需要这样的精度,但有人知道为什么我会得到两个不同的结果吗

Tanner在本文中列出的.NET Core 3.0中对浮点进行了许多更改。

我认为我们关心的是:

ToString()ToString("G")ToString("R")现在将返回最短的可往返字符串。这确保了用户最终得到的是默认情况下可以工作的东西。有问题的一个例子是Math.PI.ToString(),其中先前返回的字符串(对于ToString()ToString("G")(是3.14159265358979;相反,它应该返回3.1415926535897931。解析之前的结果时,返回的值与Math.PI的实际值内部相差7 ULP(最后一位为单位(。这意味着用户很容易陷入这样一种情况:当需要序列化/反序列化浮点值时,他们会意外地丢失浮点值的一些精度。

所以4.0584789241077042的值现在是可以往返的最短值。换言之,即使生成的字符串缺少最后一个小数点("4.058478924107704"(,由于与4.058478924107704最接近的值(可以由IEEE双精度表示(是4.0584789241077042,因此将其解析回双精度仍然会得到4.0584789241077042

double original = 4.0584789241077042;
Console.WriteLine("Original: {0:G17}", original);
// Original: 4.0584789241077042
string s = original.ToString("R", CultureInfo.InvariantCulture);
Console.WriteLine("Rouble-trippable: {0}", s);
// Rouble-trippable: 4.058478924107704
double parsed = double.Parse(s, CultureInfo.InvariantCulture);
Console.WriteLine("Parsed: {0:G17}", parsed);
// Parsed: 4.0584789241077042
Console.WriteLine("Original == Parsed: {0}", original == parsed);
// Original == Parsed: True

最新更新