为什么.NET Decimal.ToString的(字符串)轮远离零,与语言规范显然不一致? [英] Why does .NET decimal.ToString(string) round away from zero, apparently inconsistent with the language spec?

查看:346
本文介绍了为什么.NET Decimal.ToString的(字符串)轮远离零,与语言规范显然不一致?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我看到的是,在C#中,绕过一个小数,默认情况下,使用 MidpointRounding.ToEven 。这是预期,是C#规范规定的。不过,考虑到以下几点:




  • A 十进制DVAL

  • 系统格式为:字符串sFmt ,在传递到 dVal.ToString(sFmt)时,将导致在包含一个圆形的版本 DVAL



的字符串...它是显然, Decimal.ToString的(字符串)返回使用 MidpointRounding.AwayFromZero 的值四舍五入。这似乎是C#规范的直接矛盾



我的问题是这样的:是有一个很好的理由是这样的话?或者这只是在语言不一致?



下面,仅供参考,我已经包括了一些代码写入到控制台取整运算结果的分类和 Decimal.ToString的(字符串)运行结果,每个在小数值的数组,每个值。实际产出嵌入。在那之后,我已经包括了从的十进制键入C#语言规范部分中的相关段落。



示例代码:

 静态无效的主要(字串[] args)
{
十进制[] dArr =新小数[] {12.345米,12.355米};

OutputBaseValues(dArr);
//相应值:
// D [0] = 12.345
// D [1] = 12.355

OutputRoundedValues(dArr);
//默认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);
//与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);
//与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();
}

私有静态无效OutputBaseValues(十进制[] dArr)
{
Console.WriteLine(基本价值观:);
的for(int i = 0; I< dArr.Length;我++)Console.WriteLine(D [{0}] = {1},我,dArr [I]);
Console.WriteLine();
}

私有静态无效OutputRoundedValues(十进制[] dArr)
{
Console.WriteLine(默认MidpointRounding倒圆:);
的foreach(在dArr小数四)Console.WriteLine(Math.Round({0},2)=> {1},D,Math.Round(D,2));
的foreach(在dArr小数四)Console.WriteLine(decimal.Round({0},2)=> {1},D,decimal.Round(D,2));
Console.WriteLine();
}

私有静态无效OutputRoundedValues(十进制[] dArr,MidpointRounding MR)
{
Console.WriteLine(先生四舍五入= MidpointRounding {0}: , 先生);
的foreach(在dArr十进制D)Console.WriteLine(Math.Round({0},2,MR)=> {1},D,Math.Round(D,2,MR));
的foreach(在dArr十进制D)Console.WriteLine(decimal.Round({0},2,MR)=> {1},D,decimal.Round(D,2,MR));
Console.WriteLine();
}

私有静态无效OutputToStringFormatted(十进制[] dArr,字符串格式)
{
Console.WriteLine(Decimal.ToString的(\{0} \):,格式);
的foreach(在dArr十进制D)Console.WriteLine({0}的ToString(\{1} \)=> {2},D,格式,d.ToString(格式) );
Console.WriteLine();
}





从第4.1.7段落C#语言规范(十进制类型)(获得完整的规范的这里(.DOC)):



decimal类型值的运算结果是,这将导致从计算精确的结果(保留规模,为每个运营商定义的),然后取整到适合的代表性。结果四舍五入到最接近的可表示值,而当结果同样地接近于两个可表示值时,有一个甚至在最显著数位序数(这被称为银行家舍入)的值。零结果总是为0,标志,标度为0。

可以很容易地看到,他们可能没有考虑到的ToString(字符串)这一段,但我倾向于认为它适合在此说明。


解决方案

<罢工> 的ToString()根据文化默认格式,不按规范的计算方面。显然,文化为您的区域设置(以及大多数,从它的外观)预计,舍入远离零。



如果你想不同的行为,你可以传递一个的IFormatProvider 的ToString()



我想上面,但你是正确的,它总是舍从零无论文化之遥。检查举证评论中的链接。


I see that, in C#, rounding a decimal, by default, uses MidpointRounding.ToEven. This is expected, and is what the C# spec dictates. However, given the following:

  • A decimal dVal
  • A format string sFmt that, when passed in to dVal.ToString(sFmt), will result in a string containing a rounded version of dVal

...it is apparent that decimal.ToString(string) returns a value rounded using MidpointRounding.AwayFromZero. This would appear to be a direct contradiction of the C# spec.

My question is this: is there a good reason this is the case? Or is this just an inconsistency in the language?

Below, for reference, I've included some code that writes to console an assortment of rounding operation results and decimal.ToString(string) operation results, each on every value in an array of decimal values. The actual outputs are embedded. After that, I've included a relevant paragraph from the C# Language Specification section on the decimal type.

The example code:

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();
}


The paragraph from section 4.1.7 of the C# Language Specification ("The decimal type") (get the full spec here (.doc)):

The result of an operation on values of type decimal is that which would result from calculating an exact result (preserving scale, as defined for each operator) and then rounding to fit the representation. Results are rounded to the nearest representable value, and, when a result is equally close to two representable values, to the value that has an even number in the least significant digit position (this is known as "banker’s rounding"). A zero result always has a sign of 0 and a scale of 0.

It's easy to see that they may not have been considering ToString(string) in this paragraph, but I'm inclined to think it fits in this description.

解决方案

ToString() by default formats according to the Culture, not according to a computational aspect of the specification. Apparently the Culture for your locale (and most, from the looks of it) expects rounding away from zero.

If you want different behavior, you can pass an IFormatProvider in to ToString()

I thought the above, but you are correct that it always rounds away from zero no matter the Culture. Check the links in the comments for proof.

这篇关于为什么.NET Decimal.ToString的(字符串)轮远离零,与语言规范显然不一致?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆