[英]Rounding difference between decimal and money in .Net and SQL Server
[英]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.