在c#中,Math.Round(2.5)的结果是2。

应该是3,不是吗?为什么在c#中它是2 ?


这被称为舍入到偶数(或银行家舍入),这是一种有效的舍入策略,可以最大限度地减少总和中的累积错误(MidpointRounding.ToEven)。理论是,如果你总是在同一个方向四舍五入一个0.5的数字,误差会更快地累积(四舍五入到偶数应该是最小化)(a)。

以下是MSDN对以下内容的描述:

Math.Floor, which rounds down towards negative infinity. Math.Ceiling, which rounds up towards positive infinity. Math.Truncate, which rounds up or down towards zero. Math.Round, which rounds to the nearest integer or specified number of decimal places. You can specify the behavior if it's exactly equidistant between two possibilities, such as rounding so that the final digit is even ("Round(2.5,MidpointRounding.ToEven)" becoming 2) or so that it's further away from zero ("Round(2.5,MidpointRounding.AwayFromZero)" becoming 3).

下面的图表可能会有所帮助:

-3        -2        -1         0         1         2         3
 +--|------+---------+----|----+--|------+----|----+-------|-+
    a                     b       c           d            e

                       a=-2.7  b=-0.5  c=0.3  d=1.5  e=2.8
                       ======  ======  =====  =====  =====
Floor                    -3      -1      0      1      2
Ceiling                  -2       0      1      2      3
Truncate                 -2       0      0      1      2
Round(ToEven)            -3       0      0      2      3
Round(AwayFromZero)      -3      -1      0      2      3

请注意,Round比它看起来强大得多,因为它可以舍入到特定的小数点后数位。其他的都是0小数。例如:

n = 3.145;
a = System.Math.Round (n, 2, MidpointRounding.ToEven);       // 3.14
b = System.Math.Round (n, 2, MidpointRounding.AwayFromZero); // 3.15

对于其他函数,你必须使用乘除技巧来达到相同的效果:

c = System.Math.Truncate (n * 100) / 100;                    // 3.14
d = System.Math.Ceiling (n * 100) / 100;                     // 3.15

(a)当然,这个理论取决于这样一个事实,即你的数据在偶数一半(0.5,2.5,4.5,…)和奇数一半(1.5,3.5,…)之间的值分布相当均匀。

如果所有的“半值”都是偶数(例如),错误就会像你总是四舍五入一样快速累积。


从MSDN:

默认为Math。圆的使用 MidpointRounding.ToEven。大多数人 不熟悉“四舍五入? 甚至“作为替代”,四舍五入 “远离零”更常见 . net默认为 “四舍五入到偶数” 统计上的优势是因为 没有分享的倾向 四舍五入的意思是四舍五入 比它循环的频率稍高 向下(假设数字为 四舍五入往往是积极的。)

http://msdn.microsoft.com/en-us/library/system.math.round.aspx


使用. net对数字进行舍入可以得到您正在寻找的答案。

基本上它是这么说的:

返回值

精度等于数字的最接近的数字值。如果值位于两个数的中间,其中一个是偶数,另一个是奇数,则返回偶数。如果value的精度小于数字,则value不变地返回。

这种方法的行为遵循IEEE标准754,第4节。这种四舍五入有时被称为最接近四舍五入,或银行家四舍五入。如果数字为零,这种舍入有时被称为趋零舍入。


舍入的性质

考虑一下将一个包含分数的数字四舍五入为整数的任务。在这种情况下,舍入的过程是确定哪个整数最能代表要舍入的数字。

在普通或“算术”四舍五入中,很明显2.1、2.2、2.3和2.4四舍五入到2.0;2.6 2.7 2.8 2.9到3.0。

剩下的是2.5,与2.0相比,它更接近3.0。2.0和3.0之间的选择取决于你,两者都是同样有效的。

对于负数,-2.1、-2.2、-2.3和-2.4会变成-2.0;而-2.6、2.7、2.8和2.9在算术四舍五入下会变成-3.0。

对于-2.5,需要在-2.0和-3.0之间进行选择。

其他形式的舍入

“四舍五入”取任何小数点后的数字,并使其成为下一个“整”数。因此,不仅2.5和2.6要四舍五入到3.0,2.1和2.2也要四舍五入到3.0。

四舍五入使正数和负数都远离零。2.5到3.0和-2.5到-3.0。

“舍入”通过砍掉不需要的数字来截断数字。这样做的效果是将数字移向零。2.5到2.0和-2.5到-2.0

在“银行家四舍五入”中——最常见的形式——要四舍五入的。5要么四舍五入,要么四舍五入,这样四舍五入的结果总是偶数。因此,2.5轮到2.0,3.5轮到4.0,4.5轮到4.0,5.5轮到6.0,以此类推。

'交替四舍五入'将任何。5的过程在四舍五入和四舍五入之间交替进行。

“随机舍入”是在完全随机的基础上舍入0.5上下的数值。

对称与不对称

一个舍入函数是“对称的”,如果它把所有的数字舍入到零,或者把所有的数字舍入到零。

如果将正数舍入为零,将负数舍入为零,则函数是“不对称的”。2.5到2.0;从-2.5到-3.0。

同样不对称的还有一个函数,它把正数舍入为零,把负数舍入为零。2.5至3.0;从-2.5到-2.0。

大多数时候,人们会想到对称舍入,即-2.5会舍入到-3.0,3.5会舍入到4.0。(在c# Round(AwayFromZero))


首先,无论如何这都不是c#错误——而是。net错误。c#是一种语言——它不决定数学如何。实现了Round。

其次,不,如果你读了文档,你会发现默认的舍入是“舍入到偶数”(银行家的舍入):

Return ValueType: System.DoubleThe integer nearest a. If the fractional component of a is halfway between two integers, one of which is even and the other odd, then the even number is returned. Note that this method returns a Double instead of an integral type. RemarksThe behavior of this method follows IEEE Standard 754, section 4. This kind of rounding is sometimes called rounding to nearest, or banker's rounding. It minimizes rounding errors that result from consistently rounding a midpoint value in a single direction.

你可以指定Math。Round应该使用重载对中点进行四舍五入,重载取midpointrsurround值。这里有一个带有midpointround的重载,对应于每个没有midpointround的重载:

圆(Decimal,中角) 圆(双)/圆(双,中点) 圆(Decimal, Int32) /圆(Decimal, Int32,中角肌) 圆(Double, Int32) /圆(Double, Int32,中角)

这种默认选择是否得当则是另一回事。(midpointround只是在。net 2.0中引入的。在此之前,我不确定是否有任何简单的方法来实现所需的行为,而不是自己动手。)特别是,历史已经表明,这不是预期的行为——在大多数情况下,这是API设计中的大忌。我知道为什么银行家舍入是有用的…但这对许多人来说仍然是一个惊喜。

您可能有兴趣看看最近的Java等效枚举(RoundingMode),它提供了更多的选项。(它不只是处理中点。)


来自MSDN的数学。Round(double a)返回:

最接近a的整数 a的分数分量是一半 两个整数之间,其中一个是 偶数和另一个奇数,然后是偶数 返回Number。

... 因此,在2和3中间的2.5被四舍五入为偶数(2)。这被称为银行家四舍五入(或四舍五入为偶数),是一种常用的四舍五入标准。

同一篇MSDN文章:

此方法的行为如下 IEEE标准754,第4节。这 四舍五入有时被称为 四舍五入到最接近的,或银行家的 舍入。它最小化舍入误差 这是持续舍入的结果 一个单一的中点值 方向。

您可以通过调用Math的重载来指定不同的舍入行为。回合采取midpointround模式。


您应该检查MSDN中的Math。轮:

这种方法的行为遵循IEEE标准754,第4节。这种四舍五入有时被称为最接近四舍五入,或银行家四舍五入。

您可以指定Math的行为。使用重载进行四舍五入:

Math.Round(2.5, 0, MidpointRounding.AwayFromZero); // gives 3

Math.Round(2.5, 0, MidpointRounding.ToEven); // gives 2

这是非常丑陋的,但总是产生正确的算术四舍五入。

public double ArithRound(double number,int places){

  string numberFormat = "###.";

  numberFormat = numberFormat.PadRight(numberFormat.Length + places, '#');

  return double.Parse(number.ToString(numberFormat));

}

默认的midpointround。ToEven,或者Bankers的舍入(2.5变成2,4.5变成4等等)曾经让我在写会计报告时感到苦恼,所以我将写几句话,讲述我以前和在这篇文章中研究的发现。

那些四舍五入到偶数的银行家是谁(也许是英国银行家!)?

从维基百科

“银行家”一词的起源 四舍五入仍然比较模糊。如果这 四舍五入法曾经是一种标准 银行业,证据已经证明 很难找到。到 相反,欧洲的第2节 委员会报告的介绍 欧元和货币四舍五入 数量表明有 以前没有标准的方法 银行业的四舍五入;而且它 指定“一半”的数量 应该被围捕。

这似乎是一种非常奇怪的四舍五入的方式,尤其是对银行业来说,当然,除非银行过去常常接受大量偶数的存款。押金240万英镑,但我们算200万英镑,先生。

IEEE标准754可以追溯到1985年,并给出了两种舍入方法,但标准推荐使用银行家的舍入方法。这篇维基百科的文章列出了一长串语言如何实现舍入(如果下面有任何错误请纠正我),大多数语言都不使用Bankers的舍入,而是你在学校教过的:

C/ c++ round() from math.h舍入到0(不是银行家舍入) Java数学。舍入从0取整(它将结果取整,加0.5,强制转换为整数)。BigDecimal中有一个替代方案 Perl使用与C类似的方法 Javascript与Java的Math.Round相同。


因为Silverlight不支持midpointrsurround选项,所以你必须自己编写。喜欢的东西:

public double RoundCorrect(double d, int decimals)
{
    double multiplier = Math.Pow(10, decimals);

    if (d < 0)
        multiplier *= -1;

    return Math.Floor((d * multiplier) + 0.5) / multiplier;

}

关于如何使用它作为扩展的例子,请参阅文章:.NET和Silverlight圆整


我有这个问题,我的SQL服务器四舍五入0.5到1,而我的c#应用程序没有。所以你会看到两种不同的结果。

这是一个int/long的实现。这就是Java的舍入方法。

int roundedNumber = (int)Math.Floor(d + 0.5);

这可能也是你能想到的最有效的方法。

如果你想保持双精度并使用十进制精度,那么实际上只是使用基于小数点后多少位的10指数的问题。

public double getRounding(double number, int decimalPoints)
{
    double decimalPowerOfTen = Math.Pow(10, decimalPoints);
    return Math.Floor(number * decimalPowerOfTen + 0.5)/ decimalPowerOfTen;
}

你可以输入一个负的小数点作为小数点,这也很好。

getRounding(239, -2) = 200

以下是我必须解决的方法:

Public Function Round(number As Double, dec As Integer) As Double
    Dim decimalPowerOfTen = Math.Pow(10, dec)
    If CInt(number * decimalPowerOfTen) = Math.Round(number * decimalPowerOfTen, 2) Then
        Return Math.Round(number, 2, MidpointRounding.AwayFromZero)
    Else
        Return CInt(number * decimalPowerOfTen + 0.5) / 100
    End If
End Function

尝试使用1.905和2个小数将如预期的那样给出1.91,但Math.Round(1.905,2,MidpointRounding.AwayFromZero)给出1.90!数学。对于程序员可能遇到的大多数基本问题,Round方法是绝对不一致和不可用的。我必须检查如果(int) 1.905 * decimalPowerOfTen =数学。四舍五入(数字* decimalPowerOfTen, 2)因为我不想四舍五入什么应该四舍五入下来。


Silverlight不支持midpointrsurround选项。 下面是Silverlight的一个扩展方法,它添加了midpointrsurround枚举:

public enum MidpointRounding
{
    ToEven,
    AwayFromZero
}

public static class DecimalExtensions
{
    public static decimal Round(this decimal d, MidpointRounding mode)
    {
        return d.Round(0, mode);
    }

    /// <summary>
    /// Rounds using arithmetic (5 rounds up) symmetrical (up is away from zero) rounding
    /// </summary>
    /// <param name="d">A Decimal number to be rounded.</param>
    /// <param name="decimals">The number of significant fractional digits (precision) in the return value.</param>
    /// <returns>The number nearest d with precision equal to decimals. If d is halfway between two numbers, then the nearest whole number away from zero is returned.</returns>
    public static decimal Round(this decimal d, int decimals, MidpointRounding mode)
    {
        if ( mode == MidpointRounding.ToEven )
        {
            return decimal.Round(d, decimals);
        }
        else
        {
            decimal factor = Convert.ToDecimal(Math.Pow(10, decimals));
            int sign = Math.Sign(d);
            return Decimal.Truncate(d * factor + 0.5m * sign) / factor;
        }
    }
}

来源:http://anderly.com/2009/08/08/silverlight-midpoint-rounding-solution/


使用自定义舍入

public int Round(double value)
{
    double decimalpoints = Math.Abs(value - Math.Floor(value));
    if (decimalpoints > 0.5)
        return (int)Math.Round(value);
    else
        return (int)Math.Floor(value);
}

简单的方法是:

Math.Ceiling(decimal.Parse(yourNumber + ""));