C# 为什么.Net在String.Format中使用与默认Math.Round()算法不一致的舍入算法?

C# 为什么.Net在String.Format中使用与默认Math.Round()算法不一致的舍入算法?,c#,.net,math,rounding,string-formatting,C#,.net,Math,Rounding,String Formatting,我注意到C#/.NET中存在以下不一致之处。我想知道为什么会这样 Console.WriteLine("{0,-4:#.0} | {1,-4:#.0}", 1.04, Math.Round(1.04, 1)); Console.WriteLine("{0,-4:#.0} | {1,-4:#.0}", 1.05, Math.Round(1.05, 1)); Console.WriteLine("{0,-4:#.0} | {1,-4:#.0}", 1.06, Math.Round(1.06, 1))

我注意到C#/.NET中存在以下不一致之处。我想知道为什么会这样

Console.WriteLine("{0,-4:#.0} | {1,-4:#.0}", 1.04, Math.Round(1.04, 1));
Console.WriteLine("{0,-4:#.0} | {1,-4:#.0}", 1.05, Math.Round(1.05, 1));
Console.WriteLine("{0,-4:#.0} | {1,-4:#.0}", 1.06, Math.Round(1.06, 1));
Console.WriteLine("{0,-4:#.0} | {1,-4:#.0}", 1.14, Math.Round(1.14, 1));
Console.WriteLine("{0,-4:#.0} | {1,-4:#.0}", 1.15, Math.Round(1.15, 1));
Console.WriteLine("{0,-4:#.0} | {1,-4:#.0}", 1.16, Math.Round(1.16, 1));
Console.WriteLine();
Console.WriteLine("{0,-4:#.0} | {1,-4:#.0}", 1.04, Math.Round(1.04, 1, MidpointRounding.AwayFromZero));
Console.WriteLine("{0,-4:#.0} | {1,-4:#.0}", 1.05, Math.Round(1.05, 1, MidpointRounding.AwayFromZero));
Console.WriteLine("{0,-4:#.0} | {1,-4:#.0}", 1.06, Math.Round(1.06, 1, MidpointRounding.AwayFromZero));
Console.WriteLine("{0,-4:#.0} | {1,-4:#.0}", 1.14, Math.Round(1.14, 1, MidpointRounding.AwayFromZero));
Console.WriteLine("{0,-4:#.0} | {1,-4:#.0}", 1.15, Math.Round(1.15, 1, MidpointRounding.AwayFromZero));
Console.WriteLine("{0,-4:#.0} | {1,-4:#.0}", 1.16, Math.Round(1.16, 1, MidpointRounding.AwayFromZero));
输出:

1.0  | 1.0
1.1  | 1.0
1.1  | 1.1
1.1  | 1.1
1.2  | 1.2
1.2  | 1.2

1.0  | 1.0
1.1  | 1.1
1.1  | 1.1
1.1  | 1.1
1.2  | 1.2
1.2  | 1.2

默认的字符串格式行为似乎是使用MidpointRounding.AwayFromZero而不是Math.round()的默认值MidpointRounding.ToEven.

请查看以下内容:

WriteLine()只调用Object.ToString(),这最终导致调用Number.FormatDouble(这,null,NumberFormatInfo.CurrentInfo)。如您所见,格式字符串的参数为null。如果您想从ToString()中获取真实内容,必须使用System.Diagnostics.Debug.WriteLine(n.ToString(“R”))

“使用此说明符格式化单值或双值时,首先使用通用格式进行测试,双精度为15位,单精度为7位。如果成功地将该值解析回相同的数值,则使用通用格式说明符对其进行格式化。如果未成功将该值解析回相同的数值,则将使用17位精度(双精度)和9位精度(单精度)对其进行格式化。”

WriteLine(string,params object[])
调用
string.Format
并在当前的CultureInfo中传递,因此将使用本地化的NumberFormatInfo来确定如何写入数字。
Math.Round
不考虑区域性,因为您确切地指定了要对其进行舍入的方式


嗯,在翻遍Reflector之后,也许不是:)

作为一个历史记录,格式$的原始Visual Basic实现也与舍入到偶数(又称银行家舍入)不一致。原始格式$代码是由Tim Paterson编写的。您可能还记得Tim是一个名为QDOS(后来称为MS-DOS)的小程序的作者在那里的一段时间里,这本书相当畅销


也许这是25年来向后兼容的又一个例子。

似乎这个问题比“简单的”不一致更糟糕:

double dd = 0.034999999999999996;

Math.Round(dd, 2); // 0.03
Math.Round(dd, 2, MidpointRounding.AwayFromZero); // 0.03
Math.Round(dd, 2, MidpointRounding.ToEven); // 0.03

string.Format("{0:N2}", dd); // "0.04"

这简直是胡说八道。谁知道它是从哪里得到“0.04”的。

我在CultureInfo文档中找不到它定义格式化数字时使用的舍入算法的地方。我认为这个问题与双精度或舍入triping无关。我只想String.Format()使用中点舍入进行舍入。从零开始舍入。您使用的是
double
。使用
decimal