16

我正在使用 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

解释是什么?

4

3 回答 3

21

这是因为 .NET 默认为“ToEven”舍入,而 SQL 使用“AwayFromZero”。看到这个。这些是不同的舍入方法,它们处理 5 的方式不同。

AwayFromZero 向上舍入到下一个正数,或向下舍入到下一个负数。因此,0.5 变为 1,-0.5 变为 -1。ToEven 四舍五入到最接近的偶数。所以 2.5 变成 2,3.5 变成 4(对于负数也是如此)。5 以外的数字被视为相同,并四舍五入到最接近的数字。由于 5 与两个数字等距,因此这是一种特殊情况,具有不同的策略。

ToEven 也称为“银行规则”,它是IEEE_754中使用的默认值,这就是为什么它是 .NET 中的默认值。

相反,AwayFromZero 也称为“商业四舍五入”。我不知道为什么它是 SQL Server 的默认设置,可能仅仅是因为它是最广为人知和理解的方法。

当然,您始终可以配置您需要的内容:

在 C# 中,您可以执行以下操作:

Math.Round(value, MidpointRounding.ToEven)

或者

Math.Round(value, MidpointRounding.AwayFromZero)

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

哪种方法更好,取决于你用它做什么,以及你想要什么。对于合理的集合/分布,四舍五入的平均值与其原始值相同。AwayFromZero 不一定是这种情况。如果您有一个包含许多.5数据的集合,则舍入 AwayFromZero 会将所有这些值视为相同,并引入偏差。

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

于 2017-01-11T13:51:25.717 回答
3

添加到HoneyBadger 的答案中,您可以使用 SQLCLR(从 SQL Server 2005 开始)将 .NETMath.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

出于性能和准确性的原因,同时存在“浮点”和“小数”特定功能。FLOATT-SQL 和 CLR 上下文之间的值传输要快得多,但有时可能会在 CLR 代码中包含额外的 0.000000000005(或类似的东西),因此请务必使用与您正在使用的数据类型匹配的函数。如果您正在进行财务计算,那么您应该已经在使用DECIMAL(精确的数据类型)。如果您正在使用FLOAT(不精确的数据类型)进行财务计算,那么您应该DECIMAL尽早将其更改为;-)。

于 2019-04-05T19:26:13.717 回答
3

C# 允许您指定在中点舍入情况下要执行的操作 - 请参阅Round(Decimal, Int32, MidpointRounding)

Math.Round(0.345, 2, MidpointRounding.AwayFromZero); // returns 0.35
于 2017-01-11T13:56:59.590 回答