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_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

出于性能和准确性的原因,同时存在“浮点”和“小数”特定函数。 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# 舍入的主要内容,如果未能解决你的问题,请参考以下文章

哪个舍入舍入日期时间在 SQL 中更有效

C#格式化输出和数值舍入

如何使用 C# 舍入到最接近的千分之一

如何在c#中对以空格分隔的数字进行舍入?

如何在 MySQL 中舍入 DateTime?

如何舍入双精度值但保持尾随零