我正在将 .NET Framework 4.7.2 Visual Studio 项目移植到 .NET5.0,当值为负且接近于零时,我遇到了 double.ToString 的不同行为。
例如,双精度值 -7.1054273576010019E-15 在转换为字符串时会返回“-0”,即使我在转换之前对其进行舍入,而使用 .NET Framework 4.7.2 时,其结果是“0”(相同的代码)。
这里讨论了这种差异https://devblogs.microsoft.com/dotnet/floating-point-parsing-and-formatting-improvements-in-net-core-3-0/但我不太同意正如标题所述,这是一个改进,我相信 0 不应该有符号,它既不是正数也不是负数。
我知道我可以创建自定义 IFormatProvider 或扩展方法并根据需要转换双精度值,但这需要对代码中的每次转换进行更改,并且从事该项目的任何开发人员将来都需要使用它。在我看来,这个解决方案不是很直观,而且很可能是错误的根源。
有没有一种方法可以更轻松地恢复以前的行为? Microsoft 愿意在未来版本的 .NET 中改变这一点吗?
编辑: 我在这里的讨论中问了同样的问题https://github.com/dotnet/runtime/discussions/54537 如果对其他人有帮助的话。
从链接的帖子来看,似乎进行了更改是为了更符合 IEEE 754,这是所有语言都试图遵守的行为,以最大程度地减少混乱 - 或者至少标准化特定形式的混乱。
如果这些更改变得过于向后不兼容并大量破坏人们的程序,他们总是有可能会恢复这些更改,但如果此时尚未完成(它已经在 .NET Core 3.1 中,这是一个 LTS 版本,在 .NET 5 中)我认为不会。但在这里询问“微软是否愿意改变这一点”是错误的地方 - 在 dotnet/runtime 中提交问题并直接询问微软。
解决方法需要无处不在,但也很简单:将确切的字符串“-0”替换为“0”(例如,替换子字符串“-0”可能会导致-0.5的符号反转)。
Math.Abs
会将负零(您可以通过舍入或其他方式获得)和正零映射到 正 零。
因此,您可以使用
"-0"
,而不是将 "0"
替换为 Abs
作为字符串。
但也许你也可以这样做:
if (rounded == 0.0) {
rounded = 0.0; // POSITIVE zero
}
这应该将两种类型的零映射到正零。
在表达式内部,可以写为
rounded == 0.0 ? 0.0 : rounded
。
很清楚如何为
double
编写扩展方法,它将统一两种类型的零(并保持所有其他 double
值不变)。
相关问题在这里:
-0.1.ToString("0") 在 .NET Core 中为“-0”,在 .NET Framework 中为“0”
其被标记为以下内容的重复项:
我发现三节自定义数字格式在 .NET Framework 和 .NET 5+ 中的工作方式相同。 如果您能够添加格式参数,它可以用作解决方法。
Debug.Assert("0" == (-0.0).ToString("0.###;-0.###;0"));
// alternative forms:
Debug.Assert("0" == $"{-0.0:0.###;-0.###;0}");
Debug.Assert("0" == string.Format("{0:0.###;-0.###;0}", -0.0));