【问题标题】:Why does .NET use banker's rounding as default?为什么.NET 默认使用银行家的四舍五入?
【发布时间】:2010-09-23 15:00:57
【问题描述】:

根据文档,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 的初学者来说,这可能会产生误导。

【问题讨论】:

  • 四舍五入并不是“更自然”。自然与它无关。这只是你在小学学习“四舍五入”概念时学到的东西。小学课程并不总是描绘完整的画面。
  • @Rob 这就是为什么它更自然,即使它不正确
  • 我不明白,@Pacerier。我解释了为什么它自然,而你说这实际上就是它自然的原因。我的论点如何反对我的结论,这与你的相反?您已经习惯的事情可能感觉自然,有时我们比喻说某事是“第二天性”,但这并不能使它们自然。
  • @Rob 我说这很自然,因为感觉很自然。你知道有 36 个不同的对象具有相同的变量名 natural 对吗?
  • 自然是绝对相似的,所以用错词了;但这是迂腐的。也许“通常”会是一个更好的词......“人们通常做的四舍五入是什么”> 0.5 到 1.0

标签: .net rounding


【解决方案1】:

可能是因为它是一种更好的算法。在执行多次舍入的过程中,您将平均得出所有 0.5 的最终舍入均等。例如,如果您要添加一堆四舍五入的数字,这可以更好地估计实际结果。我想说的是,尽管这不是某些人所期望的,但这可能是更正确的做法。

【讨论】:

  • 当然假设您的奇数和偶数输入的分布是平坦的
  • +1 表示 更好的算法,虽然 Ostemar 有 actual 答案 (stackoverflow.com/questions/311696/…)
  • @Ian,我也给这个答案+1。无论如何,我们可以得到“已接受的答案”也许 OP 可以做到这一点。 “为什么”它使用这种方法的实际答案是页面的一半。虽然我非常喜欢这个答案每周一次的代表提升。
  • @Kibbee - 感谢您提出我的答案。我想由 OP 来更改他认为合适的接受答案?
  • -1 表示它是一种更好的算法。 - 给定一个使用银行家四舍五入的随机数字样本,您最终会在偶数位置拥有比奇数位置更多的数字。 - 只有在你平均这些数字之后,你才能再次获得与原始分布相似的分布。 - 但是,如果您将这些数据绘制在散点图中,则可能会看到人工分组。
【解决方案2】:

虽然我无法回答“为什么微软的设计师选择这个作为默认设置?”的问题,但我只想指出一个额外的功能是不必要的。

Math.Round 允许您指定MidpointRounding

  • ToEven - 当一个数字在两个其他数字之间时,它会向最接近的偶数四舍五入。
  • AwayFromZero - 当一个数字介于其他两个数字之间时,它会向离零最近的数字四舍五入。

【讨论】:

  • 正如我在相关线程中提到的,确保你的四舍五入是一致的——如果你有时在数据库中进行四舍五入,有时在 .net 中,你会很奇怪,一个需要数周时间才能弄清楚的分错误。
  • 一位客户曾经付给我超过 40,000 美元,以追踪两个数字之间 0.11 美元的舍入误差,这两个数字都接近 10 亿美元; 0.11 美元是由于大型机和 SQL Server 之间的第 8 位舍入误差不同所致。谈论完美主义者!
  • @EJB - 如果我要处理 10 亿美元,我可能会是一个完美主义者 ;-)
  • @E.J. Brennan:你花了 40,000 美元才弄明白?我一直看到这样的问题,四舍五入是原因#1,双/浮点规范化是原因#2,程序员错误#3 - 如果没有预定义的测试用例,#3可以立即设置为#1。顺便说一句,您能否让我联系您的亿万富翁客户,我想我也可以在他的系统中找到更多 4 万美元的错误! :D
  • @seanxe:或者如果你看过 Office Space。说真的,每当你看到金钱上神秘的、微小的不准确时,解开它们究竟是如何发生的谜团几乎总是一个好主意。您可能会决定不修复错误,但知道根本原因仍然有价值。我敢打赌,许多与金钱打交道的人都乐于注意到即使是微小的错误。
【解决方案3】:

小数多用于金钱;在处理金钱时,银行家的四舍五入很常见。或者你可以说。

主要是银行家需要 十进制类型;因此它确实 “银行家的四舍五入”

银行家四舍五入的优势在于,平均而言,如果您满足以下条件,您将获得相同的结果:

  • 在将一组“发票行”相加之前对其进行四舍五入,
  • 或将它们相加然后四舍五入

在没有计算机的时代,在相加之前进行四舍五入节省了很多工作。

(在英国,当我们采用十进制时,银行不会处理半便士,但多年来仍然存在半便士硬币,而商店的价格通常以半便士结尾——所以很多四舍五入)

【讨论】:

  • “小数主要用于金钱”...以及其他所有非整数。
  • @JohnTyree,大多数情况下,当它不是整数时,使用双精度/浮点数时不正确。见stackoverflow.com/questions/2545567/…
  • 哇。我的荒谬错误。 Decmials,是的。小数,没有。对于后人,我同意这里的原始观点。
  • 银行家可能喜欢银行家的四舍五入,但簿记员可能不喜欢它,他们说0.005的差异应该四舍五入导致0.01的四舍五入,而不是取决于它是奇数还是偶数。跨度>
【解决方案4】:

银行家算法(又名round half to even)是一个不错选择的原因的其他答案是非常正确的。在大多数合理分布上,它不会像round half away from zero 方法那样受到负面或正面偏差的影响。

但问题是为什么 .NET 使用 Banker 的实际舍入作为默认值 - 答案是 Microsoft 遵循了IEEE 754 标准。这在备注下的MSDN for Math.Round 中也有提及。

另请注意,.NET 通过提供 MidpointRounding 枚举来支持 IEEE 指定的替代方法。他们当然可以提供more alternatives 来解决关系问题,但他们选择只满足 IEEE 标准。

【讨论】:

  • 那么,为什么 IEEE 754 遵循银行家四舍五入?这个(仍然很好)答案刚刚通过。
  • @HenkHolterman 可能是由于其他答案中提到的(正如我总结的那样);它不会(太多)受到负面或正面偏见的影响,因此对于大多数分布和问题领域来说,它是一个更合理的默认值。
  • @BrandonBarkley A Decimaldecimal 浮点数,IEEE 754 确实包含 十进制浮点数。
  • @HenkHolterman : 或者有人可能会说 Microsoft 通过了存储桶
【解决方案5】:

像这样使用另一个重载的 Round 函数:

decimal.Round(2.5m, 0,MidpointRounding.AwayFromZero)

它会输出3。如果你使用

decimal.Round(2.5m, 0,MidpointRounding.ToEven)

你会得到银行家的四舍五入。

【讨论】:

  • 这没有回答为什么银行家的四舍五入被选为默认值的问题。
猜你喜欢
  • 2023-03-22
  • 1970-01-01
  • 1970-01-01
  • 2011-03-07
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多