在C#中,结果Math.Round(2.5)
为2。
应该是3,不是吗?为什么在C#中改为2?
Math.Round(2.5, 0, MidpointRounding.AwayFromZero);
1.005
不能完全以双精度表示。可能是1.00499...
。如果使用Decimal
此问题将消失。IMO的Math.Round重载的存在需要双倍的十进制数字,这是IMO的可疑设计选择,因为它很少能以有意义的方式工作。
在C#中,结果Math.Round(2.5)
为2。
应该是3,不是吗?为什么在C#中改为2?
Math.Round(2.5, 0, MidpointRounding.AwayFromZero);
1.005
不能完全以双精度表示。可能是1.00499...
。如果使用Decimal
此问题将消失。IMO的Math.Round重载的存在需要双倍的十进制数字,这是IMO的可疑设计选择,因为它很少能以有意义的方式工作。
Answers:
首先,无论如何这都不是C#错误-而是.NET错误。C#是语言-它不确定如何Math.Round
实现。
其次,否-如果您阅读了docs,则会看到默认的舍入是“四舍五入”(庄家的舍入):
返回值
类型:System.Double
最接近a的整数。如果a的小数部分位于两个整数的中间,其中一个为偶数,另一个为奇数,则返回偶数。请注意,此方法返回aDouble
而不是整数类型。备注
此方法的行为遵循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设计中的一个主要罪过。我可以看到“ 银行家四舍五入” 为何有用...但是,这仍然令许多人感到惊讶。
您可能有兴趣看一下最近的Java等效枚举(RoundingMode
),它提供了更多选项。(它不仅处理中点。)
这被称为舍入到偶数(或银行家的舍入),这是一种有效的舍入策略,用于最大程度地减少应计错误(MidpointRounding.ToEven)
。从理论上讲,如果您始终在同一方向上四舍五入一个0.5数,则误差会更快地累积(应该采用舍入到偶数的方法将其最小化)(a)。
请通过以下链接获取有关以下内容的MSDN描述:
Math.Floor
,向下舍入为负无穷大。Math.Ceiling
,向上取整为正无穷大。Math.Truncate
,向上或向下舍入为零。Math.Round
,四舍五入到最接近的整数或指定的小数位数。您可以指定行为是否在两种可能性之间完全等距,例如四舍五入,以使最终数字为偶数(“ Round(2.5,MidpointRounding.ToEven)
”变为2)或使其远离零(“ Round(2.5,MidpointRounding.AwayFromZero)
”变为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
它的功能要比看起来强大得多,这仅仅是因为它可以舍入到特定数量的小数位。其他所有的数字总是四舍五入到零。例如:
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,...)上具有相当均匀的值分布这一事实。
如果所有 “半值”都是偶数(例如),则错误的累积速度将与您始终舍入时一样快。
e
勾号(= 2.8)是否应该比2
勾号更远?
最接近a的整数。如果a的小数部分位于两个整数的中间,其中一个为偶数,另一个为奇数,则返回偶数。
...因此介于2和3之间的2.5会四舍五入为偶数(2)。这称为银行家舍入(或四舍五入),是常用的舍入标准。
相同的MSDN文章:
此方法的行为遵循IEEE标准754,第4节。这种舍入有时称为“四舍五入到最接近的舍入”或“银行家的舍入”。它最大程度地减少了由于在单个方向上一致地舍入中点值而导致的舍入误差。
您可以通过调用采用某种MidpointRounding
模式的Math.Round重载来指定其他舍入行为。
您应该检查MSDN 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。您可以选择在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也是如此。
向上舍入会使正数和负数都远离零。例如。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等。
“备用四舍五入”会在四舍五入和四舍五入之间交替任何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变为4,依此类推)在编写会计报告之前让我很吃惊,所以我将写一些我以前发现的内容以及从中寻找的内容这个帖子。
来自维基百科
银行家四舍五入一词的由来仍然比较晦涩。如果这种四舍五入方法曾经是银行业的标准,那么事实证明极难找到证据。相反,欧洲委员会报告《欧元的介绍和货币进位舍入》的第2节表明,以前没有标准的银行进位舍入方法。它指定应将“中途”金额四舍五入。
尤其对于银行而言,这似乎是一种非常奇怪的四舍五入方法,除非银行当然会使用收取偶数金额的大量存款。存入240万英镑,但我们称其为200万英镑。
IEEE 754标准可以追溯到1985年,并给出了两种取整方法,但是标准建议采用银行家标准。这篇Wikipedia文章列出了语言如何实现舍入的长长列表(如果以下任何一项有误,请纠正我),并且大多数不使用Bankers',而是在学校学习的舍入方法:
从MSDN:
默认情况下,Math.Round使用MidpointRounding.ToEven。大多数人不熟悉“四舍五入”作为替代方法,在学校更常教授“从零舍入”。.NET默认使用“四舍五入”,因为它在统计上是优越的,因为它不倾向于“四舍五入”而不是四舍五入的趋势(假设四舍五入的数字趋于正数)。 )
http://msdn.microsoft.com/zh-CN/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服务器将C#应用程序没有将其四舍五入为0.5到1。因此,您将看到两个不同的结果。
这是一个带有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
简单的方法是:
Math.Ceiling(decimal.Parse(yourNumber + ""));
这篇文章有您正在寻找的答案:
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);
}
>.5
产生与相同的行为Math.Round
。问题是当小数部分正好是零时发生0.5
。Math.Round允许您指定所需的舍入算法
这很丑陋,但总是会产生正确的算术舍入。
public double ArithRound(double number,int places){
string numberFormat = "###.";
numberFormat = numberFormat.PadRight(numberFormat.Length + places, '#');
return double.Parse(number.ToString(numberFormat));
}
Math.Round
并指定要四舍五入的方式也是如此。
这是我必须解决的方法:
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进行测试,将得到预期的1.91,但Math.Round(1.905,2,MidpointRounding.AwayFromZero)
得到1.90!对于程序员可能会遇到的大多数基本问题,Math.Round方法绝对不一致且无法使用。我必须检查是否 (int) 1.905 * decimalPowerOfTen = Math.Round(number * decimalPowerOfTen, 2)
因为我不想向上舍入应该舍入的内容。
Math.Round(1.905,2,MidpointRounding.AwayFromZero)
返回1.91