ChatGPT解决这个技术问题 Extra ChatGPT

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

根据文档,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 个不同的对象具有相同的变量名吗?
自然是绝对相似的,所以用错词了;但这是迂腐的。也许“通常”会是一个更好的词......“人们通常做的四舍五入是什么”> 0.5 到 1.0

O
Ostemar

其他解释为什么银行家算法(又名 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 Decimaldecimal 浮点数,IEEE 754 确实包含 十进制 浮点数。
@HenkHolterman:或者有人可能认为微软通过了桶
K
Kibbee

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


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

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

Math.Round 允许您指定 MidpointRounding

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

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


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

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

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

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

在将它们相加之前将一组“发票行”四舍五入,

或将它们相加然后四舍五入

在计算机出现之前的日子里,加起来之前的四舍五入节省了很多工作。

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


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

使用 Round 函数的另一个重载,如下所示:

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

它将输出 3。如果你使用

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

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


这没有回答为什么选择银行家四舍五入作为默认值的问题。