代码之家  ›  专栏  ›  技术社区  ›  zgirod

Sql Server精度狂热

  •  5
  • zgirod  · 技术社区  · 14 年前

    我对sql server的精度有问题。

    我有以下疑问:

    DECLARE @A numeric(30,10)
    DECLARE @B numeric(30,10)
    SET @A = 20.225
    SET @B = 53.3875
    SELECT @A * @B
    
    DECLARE @A1 numeric(30,14)
    DECLARE @B1 numeric(30,14)
    SET @A1 = 20.225
    SET @B1 = 53.3875
    SELECT @A1 * @B1
    
    DECLARE @A3 numeric(30,15)
    DECLARE @B3 numeric(30,15)
    SET @A3 = 20.225
    SET @B3 = 53.3875
    SELECT @A3 * @B3
    
    DECLARE @A2 numeric(20,15)
    DECLARE @B2 numeric(20,15)
    SET @A2 = 20.225
    SET @B2 = 53.3875
    SELECT @A2 * @B2
    
    DECLARE @A4 float
    DECLARE @B4 float
    SET @A4 = 20.225
    SET @B4 = 53.3875
    SELECT @A4 * @B4
    

    分别得出以下结果:

    1079.762188号

    1079.762188号

    1079.7621875个

    1079.7621875000000000000000

    1079.7621875个

    正确答案是:1079.7621875。

    我不明白为什么,当类型具有相同的签名时,它们会失去精确性。另外,为什么从30,14到30,15可以解决精度问题?另外,为什么20,15比30,15有更多的小数?

    我读过这篇文章 http://msdn.microsoft.com/en-us/library/ms190476(SQL.90).aspx 我想我应该没事,因为我的变量有同样的精度。

    任何帮助都将不胜感激!

    2 回复  |  直到 14 年前
        1
  •  2
  •   Sir Wobin    14 年前

    答案在于计算机如何在内部表示数字。根据使用的精度,SQL Server将分配5、9、13或17个字节来表示您的数字(请参见 http://msdn.microsoft.com/en-us/library/ms187746(v=SQL.90).aspx )因此,例如,当您从精度30移动到精度20时,内部表示从17字节移动到13字节。如何在17字节的数字上设置小数位数,而在13字节的数字上,数字表示的更大比例专用于小数位数(15/30=0.5,15/20=0.75),则会更改舍入行为。没有完美的答案。我们拥有的数字类型对于大多数应用程序来说已经足够好了,但有时由于我们在表示计算机中的数字方面的妥协,您会看到一些奇怪的工件。

    作为旁白,要非常非常小心浮点数类型。它们只是粗略地近似数字,当使用数量时会给你非常错误的结果。当在一次计算中使用不超过20个浮点数时,它们在大多数科学应用中都是极好的。在数量上使用时,比如在一个和(列名)中添加100万个浮点数,就会得到垃圾。演示如下:

    DECLARE @f FLOAT
    DECLARE @n NUMERIC(20,10)
    DECLARE @i INT
    
    SET @f = 0
    SET @n = 0
    SET @i = 0
    
    WHILE @i < 1000000
    BEGIN
        SET @f = @f + 0.00000001
        SET @n = @n + 0.00000001
        SET @i = @i + 1
    END
    
    SELECT @n as [Numeric], @f as [Float]
    

    这在SQL Server 2008上给出了以下答案。

    Numeric      Float
    0.0100000000    0.00999999999994859
    
        2
  •  3
  •   Donnie    14 年前

    这在粘贴的链接底部很重要:

    • 结果精度和刻度绝对最大值为38。当 结果精度大于38, 相应的比例减少到 防止结果的组成部分 不会被截短。

    对于精度为30的所有结果,最终计算精度为61。由于最大可能的精度是38,由此产生的精度降低了23。因此,所有的尺度都在减小,以避免在绝对必要的情况下截断结果的积分部分。

    第二个到最后一个值,其中每个值的精度为20,结果精度为41,只需减少3,在刻度部分留下可能较轻的减少。

    (30,15)之所以有效,是因为合成比例是30,所以,当它减小时,它仍然足够大,可以容纳所需的值。

    教训:不要使精度和比例超过你需要的范围,否则你会得到奇怪的结果。