繁体   English   中英

在SQL中重新创建.NET舍入行为

[英]Recreating .NET rounding behavior in SQL

我正在尝试将一个微妙的计算从C#.NET迁移到TSQL,并且绝对每个结果必须保持不变,无论原始C#.NET答案在某些情况下是否不准确。

我的TSQL脚本大多数都是成功的,然而,在.NET中有一些我不能捕获的不寻常的舍入行为。 我将用两个例子来证明:

这是我正在尝试重新创建的.NET代码。

(double)Math.Round(number, 1, MidpointRounding.AwayFromZero );

double number = 6.1499999999999995; // returns 6.1
double number = 6.9499999999999993;  // returns 7  (which, to me, seems inconsistent) 

这是我试图与上面的.NET代码对齐的TSQL代码。

SELECT ROUND(ROUND(CAST(@Score as decimal(10,1)), 16), 1)

DECLARE @Score DECIMAL(18,16) = 6.1499999999999995 -- returns 6.1
DECLARE @Score DECIMAL(18,16) = 6.9499999999999993 -- returns 6.9 (needs to be 7)

我的目标是在第二种情况下让我的TSQL代码返回7而不是6.9。 有谁知道我怎么能做到这一点?

decimal是十进制精确数。 double不是。 由于您的C#代码使用了double ,因此在T-SQL中只复制类似行为的实际方法是在T-SQL中使用二进制浮点数 - float (具体地说, double对应于float(53) )。 舍入行为不是“不寻常”,它遵循IEEE-754标准。

但是,如果您关心小数精度(看起来应该如此),您应该真正推动切换C#代码以使用decimal而不是double 当你想要小数精度时,二进制浮点是一个糟糕的选择。

我不认为有一种可靠的方法可以使用普通的T-SQL获得你想要的东西。

你的逻辑错了:

在应用ROUND之前, CAST(@Score as decimal(10,1))已经CAST(@Score as decimal(10,1))

DECLARE @Score decimal(18,16) = 6.9499999999999993

SELECT @Score, CAST(@Score as decimal(10,1))

6.9499999999999993 6.9

然后你将ROUND为16位,然后将另一个ROUND应用于1位数,但ROUND(ROUND(n, 16), 1)可能与ROUND(@n,1)结果不同ROUND(@n,1)并且绝对不是.NET舍入的方式。

另一个问题是舍入规则,你指定了MidpointRounding.AwayFromZero ,但是对于T-SQL的默认值是Rounding to Even

您可以检查SQL Server舍入方法并尝试实现Round to Even但是仍然存在有效数字的问题, DEC(18,16)有18,但DOUBLE只有16。

如果您不能在T-SQL中使用CLR函数或在.NET中切换到DECIMAL ,那么您可能注定...

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM