.NET中最后一位数字为5时的舍入规则是什么?

obn*_*ews 45 .net c# floating-point rounding console.writeline

这是我的代码:

\n
using static System.Console;\n\nnamespace ConsoleApp2\n{\n    internal class Program\n    {\n        static void Main(string[] args)\n        {\n            double[] doubles = new[] { 9.05, 9.15, 9.25, 9.35, 9.45, 9.55, 9.65, 9.75, 9.85, 9.95 };\n            foreach (double n in doubles)\n            {\n                WriteLine("{0} ===> {1:F1}", n, n);\n            }\n\n        }\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

输出.NET Framework 4.7.2

\n
9.05 ===> 9.1\n9.15 ===> 9.2\n9.25 ===> 9.3\n9.35 ===> 9.4\n9.45 ===> 9.5\n9.55 ===> 9.6\n9.65 ===> 9.7\n9.75 ===> 9.8\n9.85 ===> 9.9\n9.95 ===> 10.0\n
Run Code Online (Sandbox Code Playgroud)\n

输出.NET 6(使用相同的代码):

\n
9.05 ===> 9.1\n9.15 ===> 9.2\n9.25 ===> 9.2\n9.35 ===> 9.3\n9.45 ===> 9.4\n9.55 ===> 9.6\n9.65 ===> 9.7\n9.75 ===> 9.8\n9.85 ===> 9.8\n9.95 ===> 9.9\n
Run Code Online (Sandbox Code Playgroud)\n

因此,在 .NET Framework 中,数字是四舍五入的,就像我们在学校学到的那样。在维基百科中被称为round half up

\n

但在 .NET 6 中,9.05, 9.15, 9.55, 9.65, 9.75是向上舍入,而9.25, 9.35, 9.45, 9.85, 9.95向下舍入。

\n

我知道有一个规则叫round half to even\xe2\x80\x93 四舍五入到最接近的值;如果数字落在中间,则会四舍五入到最接近的具有偶数最低有效数字的值。

\n

但这显然不是round half to even,有些数字被四舍五入为奇数

\n

我们如何解释 .NET Framework 4.7.2 与 .NET 6 中的差异以及如何以与 .NET 6 中的 .NET Framework 相同的方式对数字进行舍入?

\n

Dav*_*oft 53

使用小数,而不是双精度,否则您不会以您认为的确切值开始,并且不会得到预期的结果。

9.05 ===> 9.1
9.15 ===> 9.2
9.25 ===> 9.3
9.35 ===> 9.4
9.45 ===> 9.5
9.55 ===> 9.6
9.65 ===> 9.7
9.75 ===> 9.8
9.85 ===> 9.9
9.95 ===> 10.0
Run Code Online (Sandbox Code Playgroud)

对于双精度数,大多数值与代码中的十进制文字略有偏差,因此四舍五入到最接近的数字。实际上只有两个处于中点,并且在 .NET Core 中四舍五入为偶数。但正如 @Traveller 指出的那样,这不是一般的舍入行为;它特定于浮点数的打印方式。

9.05000000000000071E+000 ===> 9.1 <- rounded to nearest
9.15000000000000036E+000 ===> 9.2 <- rounded to nearest
9.25000000000000000E+000 ===> 9.2 <- rounded to even
9.34999999999999964E+000 ===> 9.3 <- rounded to nearest
9.44999999999999929E+000 ===> 9.4 <- rounded to nearest
9.55000000000000071E+000 ===> 9.6 <- rounded to nearest
9.65000000000000036E+000 ===> 9.7 <- rounded to nearest
9.75000000000000000E+000 ===> 9.8 <- rounded to even
9.84999999999999964E+000 ===> 9.8 <- rounded to nearest
9.94999999999999929E+000 ===> 9.9 <- rounded to nearest
Run Code Online (Sandbox Code Playgroud)

  • @AndrewMorton 我不同意。如果代码对十进制值进行操作,例如 9.25,则建议更改所使用的类型是给出的正确答案。 (28认同)
  • 这似乎并不能解释在问题中观察到的差异。更改变量的类型是作弊行为;)是否有官方文档支持更改? (10认同)
  • @AndrewMorton 使用双打,然后预期的精确结果总是错误的,如果您需要以受控方式舍入,请使用双打。/sf/ask/41160311/ (6认同)
  • @tymtam:任何其他数字都是更好的例子。“9.25”*可以*精确地表示为二进制浮点“double”,因为它可以表示为具有 2 次方分母的分数。(在 https://www.h-schmidt.net/FloatConverter/IEEE754.html 中尝试单精度浮点数)。像 9.15 这样的源值是一个问题,正如您在 David 的表中看到的,该表由最接近的双精度值表示为 9.15、9.25 等(即在编译时对源代码应用舍入到最近值的结果)文本,在转换为字符串期间运行时舍入之前。) (3认同)

Tra*_*ler 38

Microsoft 文档将此信息小心地隐藏在标准数字格式字符串页面中(它也可能在其他地方,但不在 Double.ToString 文档中)。

以下是供后代使用的重要摘录:

当精度说明符控制结果字符串中的小数位数时,结果字符串反映四舍五入到最接近无限精确结果的可表示结果的数字。如果有两个同样接近的可表示结果:

  • 在 .NET Framework 和 .NET Core 直至 .NET Core 2.0 上,运行时选择具有较大最低有效数字的结果(即使用MidpointRounding.AwayFromZero)。

  • 在 .NET Core 2.1 及更高版本上,运行时选择具有偶数最低有效数字的结果(即使用MidpointRounding.ToEven)。

由于 .Net 5 及更高版本大部分都延续了 Core 系列,尽管微软对它们如何合并的声明令人困惑,所以这显然属于第二种情况。

  • 因为“9.05000000000000071054”比“9.0”更接近“9.1”。MidpointRounding 仅当您实际位于中点时才适用,例如“9.25000000000000000” (15认同)
  • @obnews - 从零舍入并仅使用正数(例如您在学校记得的舍入)的一个问题是您没有意识到舍入规则*仅*适用于您恰好位于中点的情况。您无需调用舍入规则即可知道“9.59”比“9”更接近“10”。因此,当您使用不同的舍入规则时,您仍然不会将“9.59”舍入为“9”。 (9认同)
  • 有关 .NET 舍入的更多信息,请参阅 [Math.Round](https://learn.microsoft.com/en-us/dotnet/api/system.math.round?view=net-6.0#remarks) 页面。 (2认同)