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

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

它应该是3,不是吗? 为什么是2而不是C#?

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

其次,不,如果你阅读文档 ,你会发现默认的四舍五入是“舍入”(银行家的四舍五入):

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

备注
这种方法的行为遵循IEEE标准754第4节。这种四舍五入有时被称为四舍五入到四舍五入或四舍五入。 它可以减less由于在一个方向上不断舍入中点值而导致的舍入误差。

您可以指定Math.Round应该如何使用带有MidpointRounding值的重载 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)

这种违约是否被恰当地select是另外一回事。 ( MidpointRounding仅在.NET 2.0中引入,在此之前,我不确定是否有任何简单的方法来实现所需的行为,而无需亲自执行。)特别是,历史logging显示,这不是预期的行为 – 并且在大多数情况下这是APIdevise中的一个主要罪过。 我可以明白为什么银行家的四舍五入是有用的…但它仍然是一个惊喜,许多人。

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

这就是所谓的四舍五入(或银行家的四舍五入),这是一个有效的舍入策略,最大限度地减less累计和(MidpointRounding.ToEven)错误。 理论是,如果你总是在同一个方向上舍入一个0.5的数字,那么错误会更快地产生(圆到平均应该尽量减less) (a)

请按以下链接查看以下MSDN描述:

  • Math.Floor ,向着无穷大的方向发展。
  • Math.Ceiling ,正向无穷大迈进。
  • Math.Truncate ,向上Math.Truncate到零。
  • Math.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 

与其他function,你必须使用乘法/除法,以达到相同的效果:

 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,…)之间具有相当均匀的价值分布的事实。

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

从MSDN,Math.Round(double a)返回:

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

…和2.5,在2和3之间的中间,向下舍入到偶数(2)。 这被称为银行家舍入 (round-to-even),是一个常用的舍入标准。

同样的MSDN文章:

这种方法的行为遵循IEEE标准754第4节。这种四舍五入有时被称为四舍五入到四舍五入或四舍五入。 它可以减less由于在一个方向上不断舍入中点值而导致的舍入误差。

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

你应该检查Math.Round MSDN:

这种方法的行为遵循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。 这取决于你select2.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之间select。

其他forms的舍入

“取整”取小数点后的数字,并作为下一个“整数”。 因此,不仅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 – 被四舍五入的四舍五入被舍入或舍入,舍入的结果总是偶数。 从而2.5轮到2.0,3.5到4.0,4.5到4.0,5.5到6.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)

默认的MidpointRounding.ToEven ,或者银行家的四舍五入( 2.5变成2,4.5变成等等 )之前已经为我的会计写了报告,所以我会写一些我以前发现的东西,从看进入这个职位。

这些银行家究竟是谁呢?甚至是英国的银行家呢?

从维基百科

银行家四舍五入这个词的起源仍然比较模糊。 如果这个四舍五入方法曾经是银行业的一个标准,证据就很难find。 相反,欧盟委员会报告“欧元导入”和“货币数量四舍五入”的第2节表明,以前没有标准的四舍五入方法, 并且规定了“中途”的数额应该四舍五入。

这似乎是一个非常奇怪的四舍五入的方法,除非银行当然会使用大量的存款。 存款240万英镑,但我们会把它称为200万英镑先生。

IEEE 754标准的历史可以追溯到1985年,并给出了两种舍入方式,但是银行家则是按照标准推荐的。 这个维基百科文章有一个很长的清单,语言如何实现四舍五入(纠正我,如果任何下面的错误),大多数不使用银行家',但四舍五入你在学校教:

  • 来自math.h的C / C ++ round()从零开始(不是银行家的四舍五入)
  • Java Math.Round从零开始(它将结果放置到底 ,加0.5,将其转换为整数)。 在BigDecimal中有一个select
  • 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; } 

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

我有这个问题,我的SQL服务器凑到0.5到1,而我的C#应用​​程序没有。 所以你会看到两个不同的结果。

这是一个int / long的实现。 这就是Java的轮stream。

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

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

如果你想保留一个double并使用十进制精度,那么它实际上只是基于多less个小数位而使用10的指数。

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

你可以input一个小数点的负数,也是很好的。

 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); } /// <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 + "")); 

这是我不得不解决的方法:

 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! Math.Round方法绝对不一致,对于程序员可能遇到的大多数基本问题都是无法使用的。 我必须检查是否(int) 1.905 * decimalPowerOfTen = Math.Round(number * decimalPowerOfTen, 2)因为我不想总结什么应该(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)); }