SQL Server:使用数字文字计算

时间:2009-07-02 06:34:28

标签: sql-server-2005 floating-point precision

我使用浮点计算进行了一些测试,以最大限度地减少精度损失。我偶然发现了一个我想在这里展示的现象,并希望得到一个解释。

写作时

print 1.0 / (1.0 / 60.0)

结果是

60.0024000960

当我编写相同的公式并对float

进行显式转换时
print cast(1.0 as float) / (cast(1.0 as float) / cast(60.0 as float))

结果是

60

到目前为止,我认为带有小数位的数字文字会自动被视为具有适当精度的float值。投射到real会显示与投射到float相同的结果。

  • 是否有一些关于SQL Server如何评估数字文字的文档?
  • 这些文字的数据类型是什么?
  • 我是否真的必须将它们转换为float以获得更好的精确度(这对我来说听起来很讽刺)?
  • 是否有一种比使用演员表混乱我的公式更简单的方法?

4 个答案:

答案 0 :(得分:9)

SQL Server使用尽可能小的数据类型。

运行此脚本时

SELECT SQL_VARIANT_PROPERTY(1.0, 'BaseType')
SELECT SQL_VARIANT_PROPERTY(1.0, 'Precision')
SELECT SQL_VARIANT_PROPERTY(1.0, 'Scale')
SELECT SQL_VARIANT_PROPERTY(1.0, 'TotalBytes')

您将看到SQL Server隐式使用NUMERIC(2,1)数据类型 除以60.0将结果转换为NUMERIC(8,6) 最终计算将结果转换为NUMERIC(17,10)。


修改

取自SQL Server联机丛书Data Type Conversion

  

在Transact-SQL语句中,一个常量   带小数点是自动的   转换为数字数据值,   使用最小精度和比例   必要。例如,常数   12.345被转换为精度为5和a的数值   规模为3。

答案 1 :(得分:4)

是的,您经常需要将它们转换为浮动以获得更好的精度。我接受了它:

For better precision cast decimals before calculations

答案 2 :(得分:3)

我认为应该理解幕后发生的事情,以便在类似情况下进一步参考。

带小数点的文字数值,不包括科学记数法,表示十进制数据类型,它存储为最小可能的十进制类型。与Lieven Keersmaekers的相同的引用来自: https://msdn.microsoft.com/en-us/library/ms191530%28SQL.90%29.aspx#_decimal

  

在Transact-SQL语句中,带小数点的常量是   使用最小值自动转换为数字数据值   精度和规模必要。例如,常数12.345是   转换为精度为5且比例为3的数值。

小数点右侧的尾随零指定比例。小数点左边的前导零被忽略。

一些例子:

1.0  -> Decimal(2,1)
60.0 -> Decimal(3,1)
1.00 -> Decimal(3,2)
01.0 -> Decimal (2,1)

要考虑的另一点是Data Type precedence。 当运算符组合了两种不同数据类型的表达式时,数据类型优先级的规则指定具有较低优先级的数据类型将转换为具有较高优先级的数据类型。 还有一点需要考虑的是,如果我们对Decimal类型进行算术运算,那么得到的Decimal类型,即精度和比例都取决于操作数和操作本身。文档Precision, Scale, and Length中描述了这一点。

所以,你的表达式的一部分在括号中

( 1.0 / 60.0 ) is evaluated to 0.016666 and the resulting type is Decimal (8,6)

使用上面关于Decimal表达式的精度和比例的规则。此外,使用银行家的舍入或舍入为偶数。重要的是要注意使用Decimal和float类型的不同舍入。 如果我们继续表达

1.0 / 0.016666 is evaluated to 60.002400096 and the resulting type is Decimal (17,10)

因此,差异的部分原因是十进制类型使用的舍入不同于浮点数。

根据上述规则,在括号内仅使用一个角色就足够了。根据数据类型优先规则,每个其他文字都将被提升为浮动。

1.0 / (1.0 / cast(60.0 as float))

还有一个重要的事情。即使这个浮点表达式也不能计算出精确的结果只是这样前端(SSMS或其他)将值舍入(我猜)精度为6位,然后截断尾随零。因此,即1.000001变为1。

简单,不是吗?

答案 3 :(得分:0)

要编写常量浮点表达式,请尝试使用科学计数法:

select (1.0E0 / (1.0E0 / 60.0E0))

结果是60。