为什么 .NET 默认使用银行四舍五入?

问题描述 投票:0回答:5

根据文档,

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,这可能会产生误导。

.net rounding
5个回答
460
投票

其他答案以及为什么银行家算法(又名round half to equal)是一个不错的选择的原因是非常正确的。在最合理的分布上,它不会像“远离零的一半”方法那样受到负偏差或正偏差的影响。 但问题是为什么 .NET 使用 Banker 的实际舍入作为默认值 - 答案是 Microsoft 遵循了

IEEE 754

标准。 MSDN for Math.Round 的备注下也提到了这一点。 另请注意,.NET 通过提供

MidpointRounding

枚举来支持 IEEE 指定的替代方法。他们当然可以提供

更多替代方案
来解决关系,但他们选择只满足 IEEE 标准。


209
投票


92
投票

Math.Round

 允许您指定 
MidpointRounding
:

ToEven - 当一个数字介于另外两个数字之间时,它会向最接近的偶数舍入。
  • AwayFromZero - 当一个数字介于另外两个数字之间时,它会向远离零的最近数字舍入。

23
投票
金钱

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

主要是银行家需要 小数类型;因此它确实 “银行家四舍五入”

银行家四舍五入的优点是,如果您满足以下条件,平均而言您会得到相同的结果:

在将一组“发票行”相加之前将其四舍五入,
  • 或将它们相加然后四舍五入总计
  • 在计算机出现之前,先四舍五入再相加可以节省大量工作。

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


0
投票

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

它将输出
3

。如果你使用 decimal.Round(2.5m, 0,MidpointRounding.ToEven)

您将获得银行家的四舍五入。

© www.soinside.com 2019 - 2024. All rights reserved.