为什么Math.Round(2.5)返回2而不是3?

在C#中, Math.Round(2.5)的结果为2。

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

首先,这不会是一个C#错误 – 它将是一个.NET错误。 C#是语言 – 它不决定如何实现Math.Round

其次,不 – 如果你阅读文档 ,你会看到默认的舍入是“舍入到均匀”(银行家的舍入):

回报价值
键入:System.Double
最接近a的整数。 如果a的小数分量在两个整数之间,其中一个是偶数而另一个是奇数,则返回偶数。 请注意,此方法返回Double而不是整数类型。

备注
此方法的行为遵循IEEE标准754第4节。这种舍入有时称为舍入到最近,或者是银行家的舍入。 它最大限度地减少了在单一方向上始终舍入中点值所导致的舍入误差。

您可以指定Math.Round如何使用带有MidpointRounding值的重载来舍入中间点。 有一个带有MidpointRounding的重载对应于每个没有一个的重载:

  • Round(Decimal) / Round(Decimal, MidpointRounding)
  • Round(Double) / Round(Double, MidpointRounding)
  • Round(Decimal, Int32) / Round(Decimal, Int32, MidpointRounding)
  • Round(Double, Int32) / Round(Double, Int32, MidpointRounding)

是否选择此默认值是另一回事。 ( MidpointRounding仅在.NET 2.0中引入。在此之前,我不确定是否有任何简单的方法可以实现所需的行为,而无需自己动手。)特别是,历史表明它不是预期的行为 – 并且在大多数情况下这是API设计中的主要罪魁祸首。 我可以看出为什么 Banker的Rounding很有用……但对许多人来说仍然是一个惊喜。

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

这被称为舍入到均匀(或银行家的舍入),这是一种有效的舍入策略,用于最小化总和中的累积误差(MidpointRounding.ToEven) 。 理论上说,如果你总是在同一方向上对0.5的数字进行舍入,那么错误将会更快地产生(圆形到偶数应该最小化) (a)

请按照以下链接获取MSDN描述:

  • Math.Floor ,向下Math.Floor负无穷大。
  • Math.Ceiling ,向正无穷大四舍五入。
  • Math.Truncate ,向上或向下Math.Truncate为零。
  • Math.RoundMath.Round入到最接近的整数或指定的小数位数。 你可以指定行为,如果它在两种可能性之间是完全等距的,例如舍入使得最后的数字是偶数(“ Round(2.5,MidpointRounding.ToEven) ”变为2)或者它更远离零(“ Round(2.5,MidpointRounding.AwayFromZero) “成为3)。

以下图表和表格可能会有所帮助:

 -3 -2 -1 0 1 2 3 +--|------+---------+----|----+--|------+----|----+-------|-+ abcde 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比它看起来更强大,只是因为它可以舍入到特定的小数位数。 所有其他的总是小数点零。 例如:

 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.Round(double a)返回:

最接近a的整数。 如果a的小数分量在两个整数之间,其中一个是偶数而另一个是奇数,则返回偶数。

……等于2.5,介于2和3之间,向下舍入到偶数(2)。 这被称为Banker’s Rounding (或round-to-even),是一种常用的舍入标准。

相同的MSDN文章:

此方法的行为遵循IEEE标准754第4节。这种舍入有时称为舍入到最近,或者是银行家的舍入。 它最大限度地减少了在单一方向上始终舍入中点值所导致的舍入误差。

您可以通过调用采用MidpointRounding模式的Math.Round重载来指定不同的舍入行为。

您应该检查MSDN for Math.Round

此方法的行为遵循IEEE标准754第4节。这种舍入有时称为舍入到最近,或者是银行家的舍入。

您可以使用重载指定Math.Round的行为:

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

四舍五入的性质

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

通常,或“算术”舍入,很明显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之间进行选择。

其他forms的舍入

‘Rounding up’取任意小数位数,并使其成为下一个’整数’。 因此,不仅2.5和2.6轮到3.0,而2.1和2.2也是如此。

四舍五入将正数和负数从零移开。 例如。 2.5至3.0和-2.5至-3.0。

‘舍入’通过砍掉不需要的数字来截断数字。 这具有将数字移向零的效果。 例如。 2.5到2.0和-2.5到-2.0

在“银行家的舍入”中 – 以其最常见的forms – 要舍入的.5向上或向下舍入,以便舍入的结果始终为偶数。 因此2.5轮到2.0,3.5到4.0,4.5到4.0,5.5到6.0,依此类推。

‘替代舍入’在舍入和舍入之间交替任何.5的过程。

‘随机舍入’在完全随机的基础上上下调整.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)

默认的MidpointRounding.ToEven ,或者银行家的四舍五入( 2.5变成2,4.5变成4等等 )在写会计报告之前已经惹我生气了所以我会先写一些我以前发现过的话,然后再看看进入这篇文章。

这些银行家是谁,甚至是数字(英国银行家也许!)?

来自维基百科

银行家四舍五入这一术语的起源仍然较为模糊。 如果这种舍入方法曾经是银行业的标准,那么事实certificate很难找到证据。 相反,欧盟委员会报告“欧元的引入和货币金额的四舍五入”第2节表明,以前没有标准的方法来进行银行业务的四舍五入; 并指明应将“中途”金额四舍五入。

这似乎是一种非常奇怪的四舍五入的方式,特别是对于银行业务,除非银行当然用来接收大量的偶数存款。 存款240万英镑,但我们称之为200万英镑先生。

IEEE标准754可以追溯到1985年,并提供两种舍入方式,但银行家是标准推荐的方式。 这篇维基百科文章列出了一个很长的列表,列出了语言如何实现四舍五入(如果以下任何一个错误,请纠正我),大多数人不使用银行家,但是你在学校教授的四舍五入:

  • 来自math.h的C / C ++ round()从零开始(不是银行家的舍入)
  • Java Math.Round从零开始舍入(它将结果置于底层,添加0.5,转换为整数)。 BigDecimal中有另一种选择
  • Perl使用与C类似的方式
  • Javascript与Java的Math.Round相同。

来自MSDN:

默认情况下,Math.Round使用MidpointRounding.ToEven。 大多数人不熟悉“四舍五入”作为替代方案,“从零开始四舍五入”更常见于学校。 .NET默认为“舍入到均匀”,因为它在统计上更优越,因为它不具有“从零舍入”的倾向,而是比向下舍入更频繁地舍入(假设舍入的数字往往是正数)。 )

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

由于Silverlight不支持MidpointRounding选项,因此您必须自己编写。 就像是:

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

有关如何将其用作扩展的示例,请参阅post: .NET和Silverlight Rounding

我有这个问题,我的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 

这篇文章有您正在寻找的答案:

http://weblogs.asp.net/sfurman/archive/2003/03/07/3537.aspx

基本上这就是它所说的:

回报价值

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

此方法的行为遵循IEEE标准754第4节。这种舍入有时称为舍入到最近,或者是银行家的舍入。 如果数字为零,则这种舍入有时称为向零舍入。

Silverlight不支持MidpointRounding选项。 这是Silverlight的扩展方法,它添加了MidpointRounding枚举:

 public enum MidpointRounding { ToEven, AwayFromZero } public static class DecimalExtensions { public static decimal Round(this decimal d, MidpointRounding mode) { return d.Round(0, mode); } ///  /// Rounds using arithmetic (5 rounds up) symmetrical (up is away from zero) rounding ///  /// A Decimal number to be rounded. /// The number of significant fractional digits (precision) in the return value. /// 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. 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 + "")); 

这是我必须解决的方式:

 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 

尝试使用带有2位小数的1.905将得到1.91,但是Math.Round(1.905,2,MidpointRounding.AwayFromZero)得到1.90! 对于程序员可能遇到的大多数基本问题,Math.Round方法绝对不一致且无法使用。 我必须检查if (int) 1.905 * decimalPowerOfTen = Math.Round(number * decimalPowerOfTen, 2)因为我不想围绕应该舍入的内容。

这很丑陋,但始终产生正确的算术舍入。

 public double ArithRound(double number,int places){ string numberFormat = "###."; numberFormat = numberFormat.PadRight(numberFormat.Length + places, '#'); return double.Parse(number.ToString(numberFormat)); }