在C#中,Math.Round(2.5)
的结果是2。
这不应该是3吗?为什么在C#中结果却是2呢?
Math.Round
。Double
而不是整数类型。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)
:对双精度浮点数进行指定位数的小数位数舍入。RoundingMode
)感兴趣,它提供了更多选项。(它不仅处理中点。)(MidpointRounding.ToEven)
。理论上,如果您总是将0.5舍入到同一个方向,误差会更快地累积(四舍六入五凑偶可最小化这种误差)(a)。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
的刻度更靠右吗? - superjos您应该查看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
从 MSDN, Math.Round(double a) 返回:
a 最近的整数。如果 a 的小数部分恰好在两个整数中间,其中一个是偶数,另一个是奇数,则返回偶数。
...因此,2.5作为介于2和3之间的数字会向下舍入到偶数(2),这被称为银行家舍入(或向最近偶数舍入),并且是常用的舍入标准之一。
同一 MSDN 文章:
此方法的行为遵循 IEEE 标准 754 第4节。这种舍入有时被称为最近取整,或者银行家舍入。它最小化了由于在单一方向上一致地将中点值舍入而导致的舍入误差。
您可以通过调用采用 MidpointRounding 模式的 Math.Round 的重载来指定不同的舍入行为。
考虑将一个含有小数部分的数字舍入到整数,例如舍入为一个整数。在这种情况下,舍入的过程是确定哪个整数最好地代表您要舍入的数字。
在常见的或称为“算术”舍入中,很明显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也是如此。
向上取整会使正数和负数离开零点。例如,2.5会变成3.0,-2.5会变成-3.0。
“向下取整”通过截断不需要的数字来截短数字。这将使数字朝零点移动。例如,2.5变成2.0,-2.5变成-2.0。
在“银行家舍入法”中,通常情况下会将待舍入的0.5四舍五入为最接近的偶数。例如2.5将被舍入为2.0,3.5将被舍入为4.0,4.5将被舍入为4.0,5.5将被舍入为6.0等。Round(AwayFromZero)
函数,-2.5将被舍入为-3.0,而3.5将被舍入为4.0。默认的MidpointRounding.ToEven或银行家舍入(2.5变为2,4.5变为4等)曾经让我在编写会计报告时感到困扰,因此我将写下我之前发现的一些内容,并从本文中了解更多。
来自维基百科
“银行家舍入”的起源仍然很模糊。如果这种舍入方法曾经是银行业的标准,那么很难找到证据。相反,欧洲委员会报告《欧元的引入和货币金额的四舍五入》第2节指出,在银行业中先前没有任何标准的舍入方法;并且它规定“半途”金额应向上舍入。
对于银行而言,这似乎是一种非常奇怪的舍入方式,除非银行曾经接收过许多相同金额的存款。 比如:存入240万英镑,但我们称其为200万英镑先生。
IEEE 754标准可追溯至1985年,并提供两种舍入方式,但建议使用银行家舍入。 这个维基百科文章列出了语言如何实现舍入的方法(如果以下任何一种不正确,请纠正我),大多数语言都没有使用银行家舍入,而是你在学校里学到的舍入方法:
来自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的四舍五入方式。
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 + ""));
Math.Round(2.5, 0, MidpointRounding.AwayFromZero);
- Robert DurginDecimal
类型,这个问题就会消失。在我的看法中,Math.Round 方法重载,以双精度数字的小数位数为参数,是一个值得怀疑的设计选择,因为它很少能够有效地工作。 - CodesInChaos