根据文档,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;
}


有人知道这个框架设计决定背后的原因吗?

框架中是否有内置的Round-up-up-up算法?还是一些不受管理的Windows API?

对于初学者来说可能会产生误导,他们只需写decimal.Round(2.5m, 0)期望结果为3但得到2。

评论

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

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

我不明白,@ Pacerier。我解释了为什么它不自然,而您说那实际上就是为什么它自然。我的论点如何与我的结论背道而驰,这与您的结论相反?您已经习惯的事物可能会感觉很自然,有时我们会比喻地说某事是“第二自然”,但这并不能使它们自然。

@Rob我说这很自然,因为感觉很自然。您知道有36个不同的对象,它们的自然名称相同吗?

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

#1 楼

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

评论


假设您当然具有奇数和偶数输入的平面分布

– jk。
2010-1-22在13:04

+1可获得更好的算法,尽管Ostemar有实际答案(stackoverflow.com/questions/311696/…)

–伊恩·博伊德(Ian Boyd)
2011年9月9日14:01在

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

– Kibbee
2011年9月9日14:13

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

– Ostemar
2011-09-25 17:40

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

–paul23
17年11月9日在6:07

#2 楼

为什么Banker的算法(也就是约半数到偶数)是一个很好的选择的其他答案是相当正确的。对于大多数合理的发行版,它没有负值或正值偏差的程度不如从零方法取整一半。
但问题是为什么.NET使用Banker的实际取整作为默认值-答案是Microsoft遵循了IEEE 754标准。在MSDN中,Math.Round在“备注”下也提到了这一点。
还请注意,.NET通过提供MidpointRounding枚举来支持IEEE指定的替代方法。他们当然可以提供更多解决关系的替代方案,但他们选择仅满足IEEE标准。

评论


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

–汉克·霍尔特曼
13年3月14日在9:45

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

– Ostemar
13年4月12日在7:55

相关:Mathematica.SE:为什么四舍五入为整数?

–user247702
2014年4月2日在8:16



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

–布兰登·巴克利
16-2-3在14:27

@BrandonBarkley十进制或十进制是浮点数,而IEEE 754确实包含十进制浮点数。

– Pablo H
18 Mar 23 '18 at 18:27

#3 楼

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

Math.Round允许您指定MidpointRounding


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


评论


正如我在相关主题中提到的那样,请确保四舍五入是一致的-如果您有时在数据库中进行四舍五入,有时在.net中进行四舍五入,则您会遇到奇怪的1美分错误,这将使您花费数周的时间弄清楚。

–克里斯
09年3月6日在19:10

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

– E.J.布伦南
2010-1-22在13:10

@EJB-如果我处理的是十亿美元,我可能会是一个完美主义者;-)

–royse41
2010-09-10 10:52

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

– Matthieu N.
2011年3月8日在2:30

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

–布赖恩
2011年6月20日在16:52



#4 楼

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


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


银行家四舍五入的优点是,如果您平均获得以下结果:


在添加“发票行”之前先对其进行四舍五入,
或将它们相加然后对总数进行四舍五入

在累加前进行舍入可以节省大量时间,而这要花在计算机之前。

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

评论


“小数多数用于赚钱” ...以及所有其他非整数的内容。

–约翰·泰瑞(John Tyree)
2013年12月13日22:30在

@JohnTyree,大多数情况下,如果不是整数,则使用double / float的情况并非如此。参见stackoverflow.com/questions/2545567/…

–伊恩·林格罗斯(Ian Ringrose)
2013年12月14日13:08

哇。我这可笑的错误。十进制,是的。小数,否。对于后代,我同意这里的原始观点。

–约翰·泰瑞(John Tyree)
13 Dec 16'2:59



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

– JustAMartin
16年6月17日在9:55

#5 楼

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

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


它将输出3。如果使用

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


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

评论


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

–shortstuffsushi
17年4月13日在16:47