• Sergiy (8/29/2016)


    To make it really painful:

    DECLARE @Amount MONEY, @Term INT

    DECLARE @fRate float, @dRate decimal (38,28)

    SELECT @Amount = 1000000, @Term = 17*3*13*7*19*23*43*11

    SELECT @fRate = CONVERT(FLOAT, @Amount)/@Term,

    @dRate = CONVERT(DECIMAL(38,28), @Amount)/@Term

    SELECT @fRate [@fRate], @fRate * @Term / @fRate /@Term * @fRate * @Term fAmount,

    @dRate [@dRate], @dRate * @Term / @dRate /@Term * @dRate * @Term dAmount

    @fRatefAmount@dRatedAmount

    0.0010424274856246410000000.0010424274856246356996090128999589.913322

    It strikes me as odd to hear that the FLOAT / REAL datatype is "precise" when by definition it is "imprecise". The following quote is taken from the MSDN page for float and real:

    Approximate-number data types for use with floating point numeric data. Floating point data is approximate; therefore, not all values in the data type range can be represented exactly.

    What's the point in all those extra "precise" digits in decimal rate if just after 5 simple arithmetic operations you're $410 off the correct result?

    Well, I would say that your test is misleading as nobody does (or hopefully doesn't do) financial calculations in that manner. What you are calling "rate" is really the payment amount, which is a currency, which needs to be rounded to 2 decimal places. So if that is $410 off, then it is due to bad code, not a bad datatype.

    Turns out, all those 12 extra digits (taking extra 5 bytes per each value of this type) are fake, non-truthful. Just a waste of server resources with negative outcome.

    Wrong.

    1) those 12 "extra" digits should have never been there in the first place for a financial calculation.

    2) They aren't fake or wrong. They are more accurate than you think, and what you think you are seeing as the "correct" number isn't what you think it is. Take a closer look and you might see something "fishy" going on. In order to help see this, I modified one of your examples to show that the "float" value multiplied by the "term" isn't the original "amount" that you appear to be getting. What you are seeing from the "float" operation is the wacky rounding that Jeff mentioned, and this is not consistent between languages and applications.

    DECLARE @Amount MONEY, @Term INT

    DECLARE @fRate float, @dRate decimal (38,28)

    SELECT @Amount = 100000, @Term = 12*17

    SELECT@fRate = CONVERT(FLOAT, @Amount)/@Term,

    @dRate = CONVERT(DECIMAL(38,28), @Amount)/@Term

    SELECT@fRate AS [fRate], @dRate AS [dRate], @Term AS [Term];

    -- 490.196078431373 490.1960784313725490196078431372 204

    SELECTCONVERT(DECIMAL(38,0), @fRate * 1000000000000) AS [DecimalFromFloat],

    CONVERT(BIGINT, CONVERT(DECIMAL(38,0), @fRate * 1000000000000)) AS [BigIntFromDecimalFromFloat],

    CONVERT(BIGINT, CONVERT(DECIMAL(38,0), @fRate * 1000000000000)) * @Term;

    -- 490196078431373 490196078431373 100000000000000092

    SELECTCONVERT(DECIMAL(38,0), @fRate * 1000000000000000) AS [BiggerDecimalFromFloat];

    -- 490196078431372540

    A very quick sanity-check of the multiplication operation would be to take the ending digits of the "term" -- 4 from 204 -- and the "fRate" -- 3 from 490.196078431373 -- and multiply them. 4 * 3 = 12, which ends with a "2", yet your test showed that the "@fOutput" value was an even "1000000", which ends in a "0". How can that be? The results shown above for the 2nd SELECT show this by turning the FLOAT into a BIGINT (so that there can be no argument over rounding, etc).

    The 3rd SELECT shows that the FLOAT is internally represented by more digits than are being displayed when left as a FLOAT.

    FLOAT is more noticeably "imprecise" in .NET where sometimes an extra 0.000000000000000000005 is added a value. FLOAT is used when you need more than 38 digits for a number (since that is the max for DECIMAL). And when this is needed, then you give up the accuracy of DECIMAL, but at that point, it is worth the trade-off. But for financial calculations, FLOAT can get you into trouble.

    P.S. MONEY is really a DECIMAL(19, 4). There is a good reason why MS didn't use FLOAT as the underlying structure for MONEY / SMALLMONEY.

    Take care,

    Solomon...

    SQL#https://SQLsharp.com/ ( SQLCLR library ofover 340 Functions and Procedures)
    Sql Quantum Lifthttps://SqlQuantumLift.com/ ( company )
    Sql Quantum Leaphttps://SqlQuantumLeap.com/ ( blog )
    Info sitesCollations     •     Module Signing     •     SQLCLR