decimal vs double! - Which one should I use and when?

511,257

Solution 1

For money, always decimal. It's why it was created.

If numbers must add up correctly or balance, use decimal. This includes any financial storage or calculations, scores, or other numbers that people might do by hand.

If the exact value of numbers is not important, use double for speed. This includes graphics, physics or other physical sciences computations where there is already a "number of significant digits".

Solution 2

My question is when should a use a double and when should I use a decimal type?

decimal for when you work with values in the range of 10^(+/-28) and where you have expectations about the behaviour based on base 10 representations - basically money.

double for when you need relative accuracy (i.e. losing precision in the trailing digits on large values is not a problem) across wildly different magnitudes - double covers more than 10^(+/-300). Scientific calculations are the best example here.

which type is suitable for money computations?

decimal, decimal, decimal

Accept no substitutes.

The most important factor is that double, being implemented as a binary fraction, cannot accurately represent many decimal fractions (like 0.1) at all and its overall number of digits is smaller since it is 64-bit wide vs. 128-bit for decimal. Finally, financial applications often have to follow specific rounding modes (sometimes mandated by law). decimal supports these; double does not.

Solution 3

System.Single / float - 7 digits
System.Double / double - 15-16 digits
System.Decimal / decimal - 28-29 significant digits

The way I've been stung by using the wrong type (a good few years ago) is with large amounts:

  • £520,532.52 - 8 digits
  • £1,323,523.12 - 9 digits

You run out at 1 million for a float.

A 15 digit monetary value:

  • £1,234,567,890,123.45

9 trillion with a double. But with division and comparisons it's more complicated (I'm definitely no expert in floating point and irrational numbers - see Marc's point). Mixing decimals and doubles causes issues:

A mathematical or comparison operation that uses a floating-point number might not yield the same result if a decimal number is used because the floating-point number might not exactly approximate the decimal number.

When should I use double instead of decimal? has some similar and more in depth answers.

Using double instead of decimal for monetary applications is a micro-optimization - that's the simplest way I look at it.

Solution 4

Decimal is for exact values. Double is for approximate values.

USD: $12,345.67 USD (Decimal)
CAD: $13,617.27 (Decimal)
Exchange Rate: 1.102932 (Double)

Solution 5

For money: decimal. It costs a little more memory, but doesn't have rounding troubles like double sometimes has.

Share:
511,257
Soni Ali
Author by

Soni Ali

Updated on April 20, 2020

Comments

  • Soni Ali
    Soni Ali about 4 years

    I keep seeing people using doubles in C#. I know I read somewhere that doubles sometimes lose precision. My question is when should a use a double and when should I use a decimal type? Which type is suitable for money computations? (ie. greater than $100 million)

  • Michael Borgwardt
    Michael Borgwardt almost 15 years
    It's not that double is inaccurate - it has relative accuracy and can represent very large or small magnitudes that decimal cannot handle at all.
  • David
    David almost 15 years
    You don't even need very large/small values to find differences between doubles base2 approximation and actual base 10 values, many small values cannot be accurately stored. Calculate "1 - 0.1 - 0.9" (make sure the compiler doesn't optimize out the equation), and compare it to zero. You'll find that with doubles the result is something like 2e-17 instead of 0 (make sure you run a compare, as many print/ToString functions round off doubles past a certain number of decimal places to remove these types of errors).
  • Groo
    Groo over 12 years
    There is no doubt that double is not to be used when representing financial values, but what did you exactly mean when you wrote that double does not support specific rounding modes, compared to a decimal? AFAIK, Math.Round has overloads which accept the MidpointRounding parameter for both double and decimal?
  • Michael Borgwardt
    Michael Borgwardt over 12 years
    @Groo: I guess I must have looked at the .Net 1.1 API, the method was added in 2.0 - but it's still kinda pointless due to the problems with binary fractions. There's an example in the current API doc that illustrates this problem.
  • Triynko
    Triynko about 12 years
    Here's why you use Decimal for money: Double's accuracy is only 16 decimal digits, and after just a few arithmetic ops, errors will quickly accumulate large enough to creep into the 15, 14, 13, etc. digits. Rounding to "cents" requires at least one digit of full accuracy after cents digit, but really you should reserve 4 or 5 to insulate from cumulative arithmetic errors, which you CANNOT allow to corrupt the hundredths column you use to round the cents. That leaves you with 16 (total) - 2 (cents) - (4 or 5 error padding) = oh $hit only 7 (or less) reliable integer digits for your money!
  • Triynko
    Triynko about 12 years
    As a result, I wouldn't manipulate monetary values of more than $9.99 (1 integer digit), because rather than 4 or 5 digits of error accumulation padding, I'd want more like 10 or 11. Since Decimal is a 128-bit number, it gives you that kind of isolation, even with numbers in the hundreds of trillions of dollars, because it has 28-29 digits of accuracy. However, you can't go much higher than that. 999,999,999,999,999.99R (999 trillion) would require 18 digits of accuracy to round properly, and since decimal gives you 28-29, that's only 10 digits of cumulative arithmetic error insulation.
  • Triynko
    Triynko about 12 years
    Just to rub it in... if you were building a game, would you really care if the barrel of explosives you just catapulted a quarter mile across a field lands a 1/16 of an inch off target because of the cumulative errors over the hundreds of "position + (velocity * time)" steps? I doubt it.
  • Triynko
    Triynko about 12 years
    Decimal is not for exact values. Decimal provides 28-29 decimal digits of accuracy according to the documentation. Decimal does not perform analytical arithmetic and is therefore not "exact". Decimal is great for money, because even with values in the trillions of dollars, it still leaves you with 10 digits of insulation from cumulative arithmetic error, while still being able to accurately round to cents.
  • David
    David about 12 years
    To clear this up double does not have 16 digits - that is only the number of meaningful digits. Floats are based around exponents in base 2 math - some base 10 numbers are corrupted because they are an infinite series if converted to a base 2 exp, in binary float math 0.1 * 0.1 != 0.01 because 0.1 cannot be represented exactly. Math operations also lead to drift - add and subtract with dollars and cents and you can get numbers like 0.9999999999999. toString() initially hides this through rounding, but exact comparisons are broken immediately.
  • gerrit
    gerrit over 11 years
    Why is the exchange rate double and not decimal? Isn't that also simply the price of 1 USD in CAD?
  • mistertodd
    mistertodd over 11 years
    @gerrit An exchange rate is not the "price" of 1 USD in CAD. It is the ratio of the value of the two. Depending on your source determines how many decimal places you'll be given. For example, 1 USD is worth 1.0016 CAD. 1 Great Britian Pound is worth 1.5909 CAD. 1 Vietnamese Dong is worth 0.000048 CAD. It's a ratio as such cannot realistically be truncated anywhere without losing precision.
  • mistertodd
    mistertodd over 11 years
    @gerrit The 0.000048 is from the Bank of Canada. XE says one VND is worth 0.0000478405 Canadian. They are calculated as a division; which results in a floating point value.
  • avl_sweden
    avl_sweden over 11 years
    @Triynko : Note that decimal is base 10, which means it can represent monetary values like $0.30 exactly. This means that as long as you do only additions, subtractions and multiplication with integers, you will not have any round off errors at all. So it doesn't have 28 digits of accuracy, it has perfect accuracy. This is a huge difference compared to binary floating point, like double, which cannot represent $0.30 exactly.
  • supercat
    supercat about 11 years
    I could recognize the value in having a fixed-precision decimal type, but I don't like the auto-floating behavior of Decimal. If fifty people each buy one object that's "3/$1", the total collected should probably not be $16.67, but more likely either $16.50 or $17.00, depending upon whether the customers are charged $0.33 or $0.34. Although Decimal is large enough that significant loss of precision to rounding is unlikely to occur, it has no way of knowing how many digits are "important" or warning when rounding errors occur.
  • Royi Namir
    Royi Namir about 10 years
    520,532.52 has 8 significant number and 1,323,523.12 has 9 mathsfirst.massey.ac.nz/Algebra/Decimals/SigFig.htm
  • cbp
    cbp almost 10 years
    "For money, always decimal", not true - see here, in particular Jeffrey Hantin's answer: stackoverflow.com/questions/2604003/for-money-always-decimal
  • Davor
    Davor almost 10 years
    @cbp - the question you linked to is about VB library, and VB doesn't have decimal. It's completely unrelated to this discussion.
  • cbp
    cbp almost 10 years
    @Davor VB does have a decimal type (msdn.microsoft.com/en-au/library/xtba3z33.aspx), and VB6 has the Currency data type which is equivelant. Jeffrey's answer is perfectly relevant to the discussion: "Financial functions use floating-point for a few reasons: They don't internally accumulate -- they are based on a closed-form exponential/logarithmic computation, not iteration and summation over periods." How is that not relevant?
  • Noctis
    Noctis over 9 years
    integer ?! and what happens when you have $1.5 ?
  • Otto Allmendinger
    Otto Allmendinger over 9 years
    @Noctis you'll come up with a solution if you think about it
  • Noctis
    Noctis over 9 years
    :) there are many solutions, but he was talking about double vs decimal, so unless he's so far off, he'll need the decimal part...that's why your answer struck me weird.
  • user2622016
    user2622016 about 9 years
    No. Decimal is not exact. And for exchange rate in example above you should use decimal, since input and output are in base 10 (when using double there is loss of precision on base conversion, since there is no 5 in prime factorisation).
  • user2622016
    user2622016 about 9 years
    it has all the troubles with rounding: try 1m/3m + 1m/3m == 2m/3m. The main difference is - more bits for significand, and most important: no precision loss when operating on numbers with 5 in prime factorisation of divisor. Eg. 1m/5m + 1m/5m will be exactly equal to 2m/5m.
  • Admin
    Admin over 8 years
    It is also true that exchange rates are not an exact reciprocal: often you get "less money" by going in one direction than the other, such that if you converted back and forth repeatedly, the amount would eventually go to zero. This is due to the two RATES being different, not due to a fee or even rounding.
  • Solomon Rutzky
    Solomon Rutzky over 7 years
    @cbp Jeffery's answer is entirely irrelevant because he's talking about the Financial Class, not financial functions in general (I fixed his answer). You did not read his entire answer, which makes it clear that decimal is better for financial operations. He says: "By using decimal arithmetic, you avoid introducing and accumulating round-off error." and "They are intended ... for decision support, and ... have little applicability to actual bookkeeping." and "but once that amount is determined, [everything else] happens in decimal.".
  • Solomon Rutzky
    Solomon Rutzky over 7 years
    @cbp Also, as pointed out in this VB forum post, even though VB .NET has Decimal, VB 6 did not, and the Microsoft.VisualBasic.Financial functions use Double to be completely backwards compatible, not because it was the "proper" datatype for the operation.
  • Kiquenet
    Kiquenet about 7 years
    For money, always decimal ,or not true, or For money, always decimal, only for C# ?
  • Michael Chudinov
    Michael Chudinov about 7 years
    Some computer games (EVE) use integers. This gives a simple system without fraction of main money unit (cents).
  • Arijoon
    Arijoon almost 7 years
    Why not use int to represent money? divide by 100 in your views
  • Bobby Adams
    Bobby Adams over 6 years
    @Arijoon I believe that's essentially what the decimal type is doing for you, except that it can divide by numbers greater than 100 as appropriate because some systems keep more than 2 decimal places of accuracy for money. (Bitcoin, for example.) The key is that decimal type is dividing by a power of 10 whereas double is dividing by a power of 2.
  • Bobby Adams
    Bobby Adams over 6 years
    There's no reason to use int instead of decimal for accuracy purposes (maybe for performance reasons). Avoid double, but use decimal. Decimal uses a base-10 exponent so you don't encounter the same binary rounding errors that you do with double when parsing a base-10 value like 0.1.
  • Imad
    Imad almost 6 years
    Saw this line in many comparisions but not able to understand the meaning. Can you kindly elaborate? "Double cannot accurately represent many decimal fractions (like 0.1) at all "
  • Michael Borgwardt
    Michael Borgwardt almost 6 years
    @lmad: I have a website for that: floating-point-gui.de - basically it's the same reason why decimal numbers cannot accurately represent 1/3
  • Shadi Alnamrouti
    Shadi Alnamrouti over 5 years
    @MichaelBorgwardt when you said "decimal, decimal, decimal", which one should I use?
  • Mass Dot Net
    Mass Dot Net over 3 years
    The float, double, and decimal links in your post are broken. Here is a link to the latest MSDN documentation on all three numeric type aliases: docs.microsoft.com/en-us/dotnet/csharp/language-reference/…
  • Daniel Wu
    Daniel Wu almost 3 years
    @Noctis he would probably suggest cooking up a custom class where you store the whole number and the decimal number as separate integers, which "decimal" has done already, probably with much better optimization too.
  • Noctis
    Noctis almost 3 years
    @DanielWu :rofl: ...maybe he would ... had to read the entire page to ground myself, as this was pre covid 19 era , and i can barely remember what happened last week :)