我看到,在 C# 中,将 a 舍入decimal
,默认情况下使用MidpointRounding.ToEven
. 这是意料之中的,也是 C# 规范所规定的。但是,鉴于以下情况:
- 一种
decimal dVal
- 一种格式
string sFmt
,当传入 时dVal.ToString(sFmt)
,将产生一个包含四舍五入版本的字符串dVal
...很明显,decimal.ToString(string)
返回一个使用MidpointRounding.AwayFromZero
. 这似乎与 C# 规范直接矛盾。
我的问题是:这种情况有充分的理由吗?或者这只是语言的不一致?
下面,作为参考,我包含了一些代码,这些代码写入控制台各种舍入运算结果和decimal.ToString(string)
运算结果,每个结果都针对值数组中的每个decimal
值。实际输出是嵌入的。之后,我在decimal
类型的 C# 语言规范部分包含了一个相关段落。
示例代码:
static void Main(string[] args)
{
decimal[] dArr = new decimal[] { 12.345m, 12.355m };
OutputBaseValues(dArr);
// Base values:
// d[0] = 12.345
// d[1] = 12.355
OutputRoundedValues(dArr);
// Rounding with default MidpointRounding:
// Math.Round(12.345, 2) => 12.34
// Math.Round(12.355, 2) => 12.36
// decimal.Round(12.345, 2) => 12.34
// decimal.Round(12.355, 2) => 12.36
OutputRoundedValues(dArr, MidpointRounding.ToEven);
// Rounding with mr = MidpointRounding.ToEven:
// Math.Round(12.345, 2, mr) => 12.34
// Math.Round(12.355, 2, mr) => 12.36
// decimal.Round(12.345, 2, mr) => 12.34
// decimal.Round(12.355, 2, mr) => 12.36
OutputRoundedValues(dArr, MidpointRounding.AwayFromZero);
// Rounding with mr = MidpointRounding.AwayFromZero:
// Math.Round(12.345, 2, mr) => 12.35
// Math.Round(12.355, 2, mr) => 12.36
// decimal.Round(12.345, 2, mr) => 12.35
// decimal.Round(12.355, 2, mr) => 12.36
OutputToStringFormatted(dArr, "N2");
// decimal.ToString("N2"):
// 12.345.ToString("N2") => 12.35
// 12.355.ToString("N2") => 12.36
OutputToStringFormatted(dArr, "F2");
// decimal.ToString("F2"):
// 12.345.ToString("F2") => 12.35
// 12.355.ToString("F2") => 12.36
OutputToStringFormatted(dArr, "###.##");
// decimal.ToString("###.##"):
// 12.345.ToString("###.##") => 12.35
// 12.355.ToString("###.##") => 12.36
Console.ReadKey();
}
private static void OutputBaseValues(decimal[] dArr)
{
Console.WriteLine("Base values:");
for (int i = 0; i < dArr.Length; i++) Console.WriteLine("d[{0}] = {1}", i, dArr[i]);
Console.WriteLine();
}
private static void OutputRoundedValues(decimal[] dArr)
{
Console.WriteLine("Rounding with default MidpointRounding:");
foreach (decimal d in dArr) Console.WriteLine("Math.Round({0}, 2) => {1}", d, Math.Round(d, 2));
foreach (decimal d in dArr) Console.WriteLine("decimal.Round({0}, 2) => {1}", d, decimal.Round(d, 2));
Console.WriteLine();
}
private static void OutputRoundedValues(decimal[] dArr, MidpointRounding mr)
{
Console.WriteLine("Rounding with mr = MidpointRounding.{0}:", mr);
foreach (decimal d in dArr) Console.WriteLine("Math.Round({0}, 2, mr) => {1}", d, Math.Round(d, 2, mr));
foreach (decimal d in dArr) Console.WriteLine("decimal.Round({0}, 2, mr) => {1}", d, decimal.Round(d, 2, mr));
Console.WriteLine();
}
private static void OutputToStringFormatted(decimal[] dArr, string format)
{
Console.WriteLine("decimal.ToString(\"{0}\"):", format);
foreach (decimal d in dArr) Console.WriteLine("{0}.ToString(\"{1}\") => {2}", d, format, d.ToString(format));
Console.WriteLine();
}
C# 语言规范(“十进制类型”)第 4.1.7 节中的段落(在此处获取完整规范(.doc)):
对十进制类型的值进行运算的结果是计算精确结果(保留为每个运算符定义的比例)然后四舍五入以适应表示的结果。结果四舍五入到最接近的可表示值,并且当结果同样接近两个可表示值时,四舍五入到在最低有效数字位置具有偶数的值(这称为“银行家四舍五入”)。零结果的符号始终为 0,标度为 0。
很容易看出他们可能没有ToString(string)
在这一段中考虑过,但我倾向于认为它符合这个描述。