代码之家  ›  专栏  ›  技术社区  ›  orj

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

  •  26
  • orj  · 技术社区  · 16 年前

    我注意到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进行舍入。

    4 回复  |  直到 16 年前
        1
  •  12
  •   Eric Lippert    16 年前

    作为历史记录,格式$VisualBasic的原始实现也与舍入到偶数(又称银行家舍入)不一致。最初的$code格式是由Tim Paterson编写的。你可能还记得Tim是一个名为QDOS(后来被称为MS-DOS)的小程序的作者,该程序在当时相当畅销。

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

        2
  •  3
  •   osexpert    11 年前

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

    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"
    

        3
  •  2
  •   Community Mohan Dere    6 年前

    Possible Bug: Math.Round returning inconsistent results

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

    Standard Numeric Format Strings

        4
  •  0
  •   Rex M    16 年前

    WriteLine(string, params object[]) string.Format 并在当前的CultureInfo中传递,因此将使用本地化的NumberFormatInfo来确定如何写入数字。 Math.Round

    嗯,在戳过反射器之后,也许不是:)