T-SQL四舍五入与C#四舍五入的区别

21
我正在使用 Microsoft SQL Server Express 2016 编写一个 存储过程。其中一个要求是进行四舍五入。但是有时候,四舍五入出现了错误。我发现 T-SQL 的四舍五入与 C# 不完全相同,为什么呢?
比较下面的两个四舍五入方法:
In T-SQL: ROUND(0.045, 2) --> this will produce 0.05

In C#: Math.Round(0.045, 2) --> this will produce 0.04

C#为什么会产生0.04?难道不应该是0.05吗?

我该怎么做才能让C#的四舍五入等同于T-SQL的四舍五入呢?


出于好奇,我在C#中尝试了这个:

Math.Round(0.055, 2)

猜猜C#四舍五入后是多少?它四舍五入到0.06!现在,我完全困惑了!

Math.Round(0.045, 2)   // This becomes 0.04
Math.Round(0.055, 2)   // This becomes 0.06

什么是解释?

3
可能是 why-does-math-round2-5-return-2-instead-of-3 的重复问题。Jon Skeet本人的回答 ;) - J2D8T
3个回答

28
这是因为.NET默认采用'ToEven'舍入方式,而SQL使用'AwayFromZero'。请参见this。它们是不同的舍入方法,并且在处理5时有所不同。
AwayFromZero将其舍入到下一个正数,或向下舍入到下一个负数。因此,0.5变成1,-0.5变成-1。ToEven则舍入到最接近的偶数。因此,2.5变成2,3.5变成4(负数同样如此)。除了5以外的数字都被视为相同,并且舍入到最接近的数字。由于5距离两个数字相等,因此它是一个特殊情况,有不同的策略。
ToEven也称为“银行规则”,它是IEEE_754中使用的默认值,这就是为什么它是.NET中的默认值why
相反地,AwayFromZero也被称为“商业舍入”。我不知道为什么它是SQL Server的默认值,可能只是因为它是最广为人知和理解的方法。
当然,您总是可以配置所需内容:
在C#中,您可以执行:
Math.Round(value, MidpointRounding.ToEven)

或者

Math.Round(value, MidpointRounding.AwayFromZero)

在SQL中,您可以使用ROUND(), FLOOR()和/或CEILING()

哪种方法更好,取决于您使用它的目的以及您想要什么。对于合理的集合/分布,四舍五入到偶数值的平均值与其原始值相同。这在向零舍入的情况下不一定成立。如果您有许多.5数据的集合,则向上舍入将将所有这些值视为相同,并引入偏差。

效果是,四舍五入后的值的平均值与原始值的平均值不同。舍入的目的是使值更简单,同时保持相同的含义。如果平均值不匹配,则不再是这种情况;舍入后的值与原始值(略微?)不同。


2
我正在写相同的答案,只是链接不同:https://msdn.microsoft.com/zh-cn/library/system.midpointrounding(v=vs.110).aspx#Anchor_3 所以您的速度很快,先生。 - SynerCoder
1
太棒了!+1 我通过另一个回答添加了一个附录,以表扬这个答案,并展示了在T-SQL中执行此类型舍入的另一种方法 :-) - Solomon Rutzky
使用 T-SQL round 函数更快。如果您使用 .NET 函数,速度会明显变慢。 - Golden Lion

5

4
除了HoneyBadger的回答,您还可以使用SQLCLR(自SQL Server 2005起)将.NET Math.Round() 方法公开给T-SQL,以便可以在查询中使用。您可以编写此代码,或者您只需下载SQL# SQLCLR库的免费版本(由我创建,并包含免费版本中的Math_RoundToEvenFloatMath_RoundToEvenDecimal),然后执行以下操作:
SELECT ROUND(0.045, 2), SQL#.Math_RoundToEvenFloat(0.045, 2);
-- 0.050    0.04

SELECT ROUND(0.055, 2), SQL#.Math_RoundToEvenFloat(0.055, 2);
-- 0.060    0.06

出于性能和精度的原因,T-SQL中有“Float”和“Decimal”两种特定函数。 FLOAT 值在T-SQL和CLR上下文之间传输更快,但有时可能会在进入CLR代码时包含额外的0.000000000005(或类似数字),因此请确保使用与您正在使用的数据类型匹配的函数。如果您正在进行金融计算,则应该已经使用 DECIMAL(一种精确数据类型)。如果您正在使用 FLOAT(一种不精确的数据类型)进行金融计算,则应尽早将其更改为DECIMAL


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接