Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/266.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 在SQL中重新创建.NET舍入行为_C#_Sql_.net_Sql Server_Tsql - Fatal编程技术网

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

C# 在SQL中重新创建.NET舍入行为,c#,sql,.net,sql-server,tsql,C#,Sql,.net,Sql Server,Tsql,我正在尝试将一个微妙的计算从C#.NET迁移到TSQL,并且绝对每个结果都必须保持不变,不管最初的C#.NET答案在某些情况下是否不准确 我的TSQL脚本在这方面基本上是成功的,但是,在.NET中有一些不寻常的舍入行为,我一直无法捕捉到。我将用两个例子来演示: 下面是我试图重新创建的.NET代码 (double)Math.Round(number, 1, MidpointRounding.AwayFromZero ); double number = 6.1499999999999995; /

我正在尝试将一个微妙的计算从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。有人知道我如何做到这一点吗?

十进制是一个精确的十进制数<代码>双精度不是。由于您的C#代码使用的是
double
,在T-SQL中复制类似行为的唯一实际方法是在T-SQL中也使用二进制浮点数-
float
(具体来说,
double
对应于
float(53)
)。舍入行为并非“异常”,它遵循IEEE-754标准


但是,如果您关心十进制精度(似乎您应该这样做),您应该真正推动将C代码切换为使用
decimal
,而不是
double
。当您需要十进制精度时,二进制浮点是一个糟糕的选择。

我认为没有可靠的方法可以使用纯t-SQL获得您想要的结果

你的逻辑是错误的:

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
您对16位数字进行
ROUND
,然后对1位数字应用另一个
ROUND
,但是
ROUND(ROUND(n,16,1)
的结果可能与
ROUND(@n,1)
的结果不同,而且绝对不是.NET进行ROUND的方式

另一个问题是舍入规则,您可以指定
中点舍入.AwayFromZero
,但是T-SQL的默认值是

您可以检查并尝试实现舍入到偶数,但是仍然存在有效数字的问题,
DEC(18,16)
有18个,但是
DOUBLE
只有16个


如果你不能在t-SQL中使用CLR函数或在.NET中切换到
DECIMAL
,那么你可能注定要失败…

这是因为C#中的双精度是浮点,而一些十进制数字不能用二进制准确表示。如果您需要精确性和准确性,请在C#代码中使用
decimal
类型。尝试复制错误行为是错误的。您知道可以从SQL Server调用.NET函数吗?如果这真的是一个很大的问题,您可以使用现有的函数,那么就调用这个函数。(但是,请记住,.NET版本给出了错误的答案)在这种情况下,CLR不在选项中。实际上,我们正在将此计算迁移到SQL,以完全避免CLR遵守安全限制。您知道,.NET返回错误的结果(由于浮点运算),并希望TSQL返回相同的错误结果?错误的标准,很好:)但是,我尝试切换到真实的数据类型,对于第一个测试用例,它返回6.2而不是6.1。这是我的代码
DECLARE@environmentalscore2real=6.14999999995;选择ROUND(ROUND(CAST(@EnvironmentalScore2为real),16),1)
@fyodordorfranz Duh,我的坏
float(53)
double
的等价物:)
real
float(24)
,最大的4字节精度。感谢您的建议。不幸的是,虽然第二个测试用例的float(53)为6.1,但第一个测试用例的float(53)为6.9(当我需要7时)。这是我的代码:
DECLARE@Score float(53)=6.9499999999993选择舍入(舍入(CAST(@Score as float(53)),16),1)
@fyodorfranz哦,这只是意味着T-SQL总是使用估计值,而不是进行浮点舍入,即使是对浮点数(这是一件好事,因为精确的四舍五入实现是特定于平台的)。然而,根本问题仍然是,您期望从
double
中获得17个十进制数字的精度,这根本不可能-您只能得到15.95个十进制数字。如果删除一个(理想情况下,两个)在“9”中,C#和T-SQL的结果都是正确的。为什么要处理不符合数据格式的数字?多亏了这一点。作为最后的努力,我想用一些复杂的switch语句来处理这类情况,检查某些相关小数位的值,然后从中决定是否符合o向上或向下取整。你知道C代码是怎么回事吗?第一个数字向下取整为6.1,第二个数字向上取整为7?@fyodorfranz:我不知道C,也许把取整规则改为
中点取整。ToEven
有帮助,但你仍然有精度问题。你不能在C中切换到十进制吗?我不能t在C#中切换到十进制。以这种方式进行计算的决定是由一家与我没有联系的标准机构做出的。我的工作是在TSQL中重新创建他们显然不精确的执行此计算的方法,而不使用任何CLR。@fyodorfranz:如果您切换到TSQL中的浮点,可能更容易模仿。这可能很难实现与人们争论“我们定义标准,你必须遵循”。顺便说一句,理论上,理论和实践之间没有区别。但是,在实践中,有:-)不,T-SQL是将中点从零舍入,这是它知道如何做的唯一模式。