根据文档,decimal.Round
方法使用舍入到偶数算法,这在大多数应用程序中并不常见。所以我总是写一个自定义函数来做更自然的圆半算法:
public static decimal RoundHalfUp(this decimal d, int decimals)
{
if (decimals < 0)
{
throw new ArgumentException("The decimals must be non-negative",
"decimals");
}
decimal multiplier = (decimal)Math.Pow(10, decimals);
decimal number = d * multiplier;
if (decimal.Truncate(number) < number)
{
number += 0.5m;
}
return decimal.Round(number) / multiplier;
}
有人知道这个框架设计决策背后的原因吗?
在框架中是否有内置的舍入算法实现?或者可能是一些非托管Windows API?
初学者可能会误导,只是简单地写decimal.Round(2.5m, 0)
期望结果为3而不是2。
答案 0 :(得分:422)
其他答案以及为什么银行家的算法(又名round half to even)是一个不错的选择是非常正确的。与大多数合理分布相比,round half away from zero方法不会产生负偏差或正偏差。
但问题是为什么.NET使用Banker的实际舍入作为默认值 - 答案是微软遵循IEEE 754标准。在备注中的MSDN for Math.Round中也提到了这一点。
另请注意,.NET通过提供MidpointRounding
枚举来支持IEEE指定的替代方法。他们当然可以提供more alternatives解决关系,但他们选择只是符合IEEE标准。
答案 1 :(得分:188)
可能是因为它是一种更好的算法。在执行多次舍入的过程中,您将平均所有.5的最终向上和向下舍入。如果您是例如添加一堆舍入数字,则可以更好地估计实际结果。我会说即使它不是人们所期望的,但这可能是更正确的事情。
答案 2 :(得分:86)
虽然我无法回答“为什么微软的设计师选择这个作为默认设置?”的问题,但我只是想指出一个额外的功能是不必要的。
Math.Round
可让您指定MidpointRounding
:
答案 3 :(得分:23)
小数主要用于钱;使用 money 时,银行家的舍入很常见。或者你可以说。
大多数银行家都需要 十进制类型;因此确实如此 “银行家的四舍五入”
银行家四舍五入的优势在于,如果您:
平均得到相同的结果加起来之前的舍入在计算机之前的日子里节省了大量的工作。
(在英国,当我们进入十进制银行不会处理半便士,但多年来仍有半便士硬币和商店的价格经常以半便士结束 - 所以很多四舍五入)
答案 4 :(得分:0)
使用Round函数的另一个重载,如下所示:
decimal.Round(2.5m, 0,MidpointRounding.AwayFromZero)
输出 3 。如果你使用
decimal.Round(2.5m, 0,MidpointRounding.ToEven)
你会得到银行家的四舍五入。
答案 5 :(得分:0)
此外,请注意,通过格式字符串(如“ 0”)进行“四舍五入”会产生与“ Math.Round()”不同的结果。即,总是将5,.5等四舍五入:
let d, d' = 2.5, 3.5
Debug.WriteLine(Math.Round(d)) // 2.5 -> 2
Debug.WriteLine(d.ToString("0")) // 2.5 -> 3
Debug.WriteLine(Math.Round(d')) // 3.5 -> 4
Debug.WriteLine(d'.ToString("0")) // 3.5 -> 4
let dd, dd' = 2.25, 2.35
Debug.WriteLine(Math.Round(dd, 1)) // 2.25 -> 2.2
Debug.WriteLine(dd.ToString("0.0")) // 2.25 -> 2.3
Debug.WriteLine(Math.Round(dd', 1)) // 2.35 -> 2.4
Debug.WriteLine(dd'.ToString("0.0")) // 2.35 -> 2.4