Decimal(28,15) rounding madness

  • Hi,

    I'm using the following system:

    Microsoft SQL Server  2000 - 8.00.760 (Intel X86)   Dec 17 2002 14:22:05   Copyright (c) 1988-2003 Microsoft Corporation  Enterprise Edition on Windows NT 5.0 (Build 2195: Service Pack 4)

    I'm trying to do some pretty basic arithmetic, but am having problems with decimal numbers being rounded. All my variables are decimal(28,15).

    The following script demonstrates the problem. The equation should (surely?!) give @number2 as the answer.

    As part of my experimenting I introduced a CAST, which seemed to solve the problem, but it only works for certain sets of numbers! The first script demonstrates how the cast seems to fix the problem, but in the second set the cast creates it's own problem.

    -- version 1 - correct answer with cast

    begin

         declare @answer1 decimal(28,15)

         declare @answer2 decimal(28,15)

        

         declare @Number1 decimal(28,15)

         declare @Number2 decimal(28,15)

        

         set @Number1 = 108.048305000000000

         set @Number2 = 109.096373558500000

        

         set @answer1 = @Number1 * (@Number2/@Number1)

         set @answer2 = @Number1 * cast((@Number2/@Number1) as decimal(28,15))

        

         print @answer1

         print @answer2

        

    end

    go

    -- version 2 - correct answer without cast

    begin

        

         declare @answer1 decimal(28,15)

         declare @answer2 decimal(28,15)

        

         declare @Number1 decimal(28,15)

         declare @Number2 decimal(28,15)

        

         set @Number1 = 99.150000000000000

         set @Number2 = 90.454500000000000

        

         set @answer1 = @Number1 * (@Number2/@Number1)

         set @answer2 = @Number1 * cast((@Number2/@Number1) as decimal(28,15))

        

         print @answer1

         print @answer2

    end

  • Well,

    You've got to expect some arithmetic roundoff to occur.  Numbers you're getting don't seem that unreasonable.  That's the way the numerical cookie crumbles.

    One suggestion, though.  To reduce the effects of  roundoff, do all numeric calculations in a native floating point format (e.g., FLOAT), and hold off on the cast to DECIMAL (for formatting) until the last possible instant.    I haven't looked to see how many bytes SQL uses to store DECIMAL numbers internally, but my gut intuition says that is the best approach, regardless:

    -- version 3 - With FLOATs

    begin    

      

         declare @Number1 FLOAT

         declare @Number2 FLOAT

         declare @answer1 FLOAT

        

         set @Number1 = 99.150000000000000

         set @Number2 = 90.454500000000000

        

         set @answer1 = @Number1 * (@Number2/@Number1)

        

         print @Number1                          -- 99.15

         print @Number2                          -- 90.4545

         print CAST(@answer1 AS decimal(28,15))  -- 90.454499999999996 -- Some (Reasonable) Roundoff!

         print CAST(@answer1 AS decimal(28,4))   -- 90.4545 -- With Formatting, It Looks Right On the Money!

    end

     

    Regards!

    - john

     

  • You might want to have a look at "Precision, Scale, and Length" in BOL. It contains a nice table:

    OperationResult precisionResult scale *
    e1 + e2max(s1, s2) + max(p1-s1, p2-s2) + 1max(s1, s2)
    e1 - e2max(s1, s2) + max(p1-s1, p2-s2)max(s1, s2)
    e1 * e2p1 + p2 + 1s1 + s2
    e1 / e2p1 - s1 + s2 + max(6, s1 + p2 + 1)max(6, s1 + p2 + 1)

    * The result precision and scale have an absolute maximum of 38. When a result precision is greater than 38, the corresponding scale is reduced to prevent the integral part of a result from being truncated.

    SQL Server has a limited number of bytes (max. 17) to use for these values and as its algorithms won't risk to lose a significant digit, it must reduces the scale. 

    Or put it simple: You should have better luck if you don't declare your data with such an extreme precision

    --
    Frank Kalis
    Microsoft SQL Server MVP
    Webmaster: http://www.insidesql.org/blogs
    My blog: http://www.insidesql.org/blogs/frankkalis/[/url]

  • Thanks for all your help!
     
    I think you're right Frank - the best way to avoid problems is not to declare such precise data types, leaving some spare capacity in SQL Server's storage of variables for the precision needed in the calculation.

    I ended up running my script with decimal(21,15) types, and the inaccuracies were greatly reduced. Using 20,15 seemed even better, but unfortunately some numbers were over 100,000, so I needed 6 digits on the left of the decimal point.

    One strange thing I discovered was that reducing the scale (the 15 part) made the problem worse, even though the numbers I was processing generally only had 5 or 10 significant figures to the right of the decimal point.

    There is some related info in the following discussion, based on the same question:

    Thanks again!

    Sauce.

Viewing 4 posts - 1 through 3 (of 3 total)

You must be logged in to reply to this topic. Login to reply