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


271

根据文档,该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却可能会产生误导。


105
舍入不是“更自然的”。大自然与它无关。这就是您在小学时学到的“四舍五入”概念。小学课程并不总是能完整呈现。
罗伯·肯尼迪

45
@Rob这就是为什么它更自然,即使它不正确的原因
Pacerier,2011年

10
我不明白,@ Pacerier。我解释为什么它自然了,你说这其实为什么它自然的。我的论点如何我的结论背道而驰,这您的结论相反?您已经习惯了的事情可能会感觉很自然,有时我们用比喻的方式说某事是“第二自然”,但这并不能使它们自然。
罗伯·肯尼迪

16
@Rob我说这很自然,因为感觉很自然。您知道有36个具有相同变量名称的对象自然吗?
Pacerier,2011年

9
大自然绝对是类比的,所以用错了这个词;但这是在学究。也许使用“通常”将是一个更好的词。.“人们通常进行的舍入是什么”> 0.5变为1.0
为什么

Answers:


196

可能是因为它是一种更好的算法。在执行许多舍入的过程中,您将平均得出所有.5的末尾均等地舍入。例如,如果添加一堆舍入的数字,则可以更好地估计实际结果。我会说,即使这不是某些人所期望的,也可能是更正确的选择。


71
假设您当然有奇数和偶数输入的平坦分布
jk。

7
+1为更好的算法,虽然Ostemar有实际的答案(stackoverflow.com/questions/311696/...
伊恩·博伊德

2
@Ian,我也给该答案+1。无论如何,我们可以将“接受的答案”移开,也许OP可以做到这一点。“为什么”使用此方法的实际答案在页面的中间。尽管我非常喜欢销售代表提升,但我每周从这个答案中得到大约一次。
Kibbee

@Kibbee-感谢您提出我的答案。我想应该由OP来更改他认为合适的答案吗?
Ostemar 2011年

-1表示它是一个更好的算法。-给定一个使用banker舍入的随机数字样本,您最终在偶数位置的数字将大于奇数位置。-只有在对这些数字取平均后,您才能再次获得与原始分布相似的点差。-但是,例如,如果您将这些数据绘制在散点图中,则可能会看到人工分组。
paul23年

437

Banker的算法(又称“ 一半至偶数”)是一个不错的选择的其他答案很正确。在大多数合理的分布范围内,它没有负值或正值偏差的程度不及方法的舍入一半

但是问题是为什么.NET将Banker的实际舍入用作默认值-答案是Microsoft遵循了IEEE 754标准。在MSDN中,Math.Round在“备注”下也提到了这一点。

还请注意,.NET通过提供MidpointRounding枚举来支持IEEE指定的替代方法。他们当然可以提供更多解决关系的替代方案,但是他们选择仅满足IEEE标准。


17
那么,为什么IEEE 754遵循银行家四舍五入呢?这个(仍然很好)的答案刚刚过去了。
Henk Holterman

4
@HenkHolterman可能是由于其他答案中提到的内容(以及我总结的);它不会(太多)遭受负面或正面的偏见,因此,对于大多数分布和问题域而言,违约更为合理。
Ostemar


我认为这很有趣,因为IEEE 754是浮点数的标准,而十进制不是。也许它遵循IEEE 754,所以使用相同的算法将Double舍入为Decimal。
布兰登·巴克利

1
@BrandonBarkley 十进制十进制 浮点数,IEEE 754确实包含十进制浮点数。
Pablo H

88

虽然我不能回答“为什么Microsoft的设计师为什么选择此默认值?”的问题,但我只想指出,没有必要附加功能。

Math.Round允许您指定一个MidpointRounding

  • ToEven-当一个数字在两个其他数字之间时,将四舍五入到最接近的偶数。
  • AwayFromZero-一个数字在两个之间的中间时,将四舍五入为最接近零的数字。

8
就像我在相关线程中提到的那样,请确保四舍五入的一致性-如果您有时在数据库中进行四舍五入,有时在.net中进行四舍五入,您将遇到奇怪的1美分错误,这将使您花费数周的时间弄清楚。
克里斯,2009年

59
一位客户曾经向我支付了超过40,000美元,以追踪两个数字之间的0.11美元舍入误差,而这两个数字都略低于10亿美元;$ 0.11是由于大型机和SQL Server之间的第8位舍入错误有所不同。谈论完美主义者!
EJ Brennan

12
@EJB-

12
@EJ Brennan:花了4万美元弄清楚了吗?我一直看到这样的问题,四舍五入是原因#1,double / float归一化是原因#2,如果没有预定义的测试用例,则可以将程序员错误#3-#3立即设置为#1。顺便说一句,您能不能让我与您的billionare客户联系,我想我也可以在他的系统中发现更多$ 40k的错误!:D

3
@seanxe:或者您是否看过Office Space。认真地讲,每当您看到货币上的神秘细微误差时,准确地解决它们的发生方式之谜几乎总是一个好主意。您可能会决定不修复错误,但知道根本原因仍然有价值。我敢打赌,很多用钱工作的人都会高兴地注意到甚至很小的错误。
布莱恩(Brian

22

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

多数银行家需要十进制类型。因此,它进行“银行四舍五入”

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

  • 在加总之前先对一组“发票行”进行四舍五入,
  • 或加起来然后四舍五入

累加前四舍五入节省了计算机使用前的大量工作。

(在英国,当我们去十进制银行时,它们处理的不是半便士,但多年来仍然有半便士的硬币,而且商店的价格常常以半便士结尾–因此四舍五入)


8
“小数多数用于赚钱” ...以及所有其他非整数的内容。
约翰·泰瑞

3
@JohnTyree,大多数情况下,如果不是整数,则使用double / float多数情况下不正确。参见stackoverflow.com/questions/2545567/…–
伊恩·林格罗斯

3
哇。我这可笑的错误。Decmials,是的。小数,否。对于后代,我同意这里的原始观点。
约翰·泰瑞

银行家可能喜欢银行家的四舍五入,但簿记员可能不会喜欢它,他们说0.005的差异应四舍五入导致0.01的舍入,而不取决于它是奇数还是偶数。
JustAMartin

0

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

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

它将输出3。如果您使用

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

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


4
这没有回答为什么选择“银行家四舍五入”作为默认值的问题。
shortstuffsushi
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.