为什么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)); }