T-SQL 舍入与 C# 舍入
Posted
技术标签:
【中文标题】T-SQL 舍入与 C# 舍入【英文标题】:T-SQL rounding vs. C# rounding 【发布时间】:2017-01-11 13:48:03 【问题描述】:我正在使用微软SQL Server Express 2016 写一个stored procedure。要求之一是进行四舍五入。但时不时地,四舍五入是错误的。我发现 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
解释是什么?
【问题讨论】:
why-does-math-round2-5-return-2-instead-of-3 的可能重复项。乔恩·斯基特本人的回答;) 【参考方案1】:这是因为 .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 中使用的默认值,即 why 它是 .NET 中的默认值。
相反,AwayFromZero 也称为“商业四舍五入”。我不知道为什么它是 SQL Server 的默认设置,可能仅仅是因为它是最广为人知和理解的方法。
当然,你可以随时配置你需要的东西:
在 C# 中你可以这样做:
Math.Round(value, MidpointRounding.ToEven)
或
Math.Round(value, MidpointRounding.AwayFromZero)
在 SQL 中,您可以使用ROUND()、FLOOR() 和/或CEILING()。
哪种方法更好,取决于您使用它的目的以及您想要什么。对于合理的收集/分配,四舍五入到偶数的平均值与其原始值相同。 AwayFromZero 不一定是这种情况。如果您有一个包含许多 .5
数据的集合,则舍入 AwayFromZero 会将所有这些值视为相同,并引入偏差。
效果是四舍五入的平均值与原始值的平均值不同。舍入的目的是使值更简单,同时具有相同的含义。如果平均值不匹配,则不再是这种情况;四舍五入后的值与原始值有(稍微?)不同的含义。
【讨论】:
我写的是相同的答案,但链接不同:msdn.microsoft.com/en-us/library/… 所以+1 为您的速度先生 很好的答案! +1 我通过another answer 添加了一个附录作为对此的补充,展示了在 T-SQL 中执行此类舍入的另一种方法:-) 使用 tsql 回合会快得多。任何时候你使用 dot.net 函数都会明显变慢【参考方案2】:添加到 HoneyBadger's answer,您可以使用 SQLCLR(自 SQL Server 2005 起)将 .NET Math.Round()
方法公开给 T-SQL,以便在查询中使用它。
您可以自己编写代码,也可以直接下载免费版本的SQL# SQLCLR 库(我创建的,包含 Math_RoundToEvenFloat 和 Math_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
出于性能和准确性的原因,同时存在“浮点”和“小数”特定函数。 FLOAT
值在 T-SQL 和 CLR 上下文之间传输得更快,但有时可能会在 CLR 代码中包含额外的 0.000000000005(或类似的东西),因此请务必使用与您正在使用的数据类型匹配的函数。如果您正在进行财务计算,那么您应该已经在使用DECIMAL
(一种精确的数据类型)。如果您使用FLOAT
(一种不精确的数据类型)进行财务计算,您应该尽早将其更改为DECIMAL
;-)。
【讨论】:
【参考方案3】:C# 允许您指定在中点舍入情况下要执行的操作 - 请参阅 Round(Decimal, Int32, MidpointRounding)。
Math.Round(0.345, 2, MidpointRounding.AwayFromZero); // returns 0.35
【讨论】:
以上是关于T-SQL 舍入与 C# 舍入的主要内容,如果未能解决你的问题,请参考以下文章