tags:

views:

180

answers:

5

Is it appropriate to use the double type to store percentage values (for example a discount percentage in a shop application) or would it be better to use the decimal type?

+5  A: 

decimal does come at a performance cost, but it's usually worth it for financial uses. The reason it has low performance (the worst of all numeric types) is that it doesn't map directly to a hardware type. That means it requires more of the work to be done in software.

Note that it is not only an issue of size. decimal is an integer scaled by a power of 10, while the float and double types are scaled by powers of 2. That means terminating decimal values like 0.1 can be exactly represented using decimal, while they are non-terminating (and thus rounded) for float and double.

Matthew Flaschen
It would be nice to know why decimal is more costly.
alexn
Thanks for your answer. I accepted it, but then Jay Bazuzi posted a very broad answer that I think will help a lot of people seeing this post in future times. That's why I changed the accepted answer from yours to the answer of Jay Bazuzi. But I made my choice from your post. Thanks.
HCL
+8  A: 

You can probably get away with saving the discount percentage as an integer. Just store 10 or 25 or whatever, and when you need to work out the price of something:

newprice = price * discount / 100
Greg Hewgill
Occasionally, stores do like to discount things by more complex amounts, though. If a store is having a "no sales tax" sale, they need to discount by whatever number will yield the normal price after sales tax is added to it, and if sales tax is something like 9.7%, that's going to be an ugly-looking discount percentage.
Morbo
@morbo, I fear the day they discount by irregular constants or mathematical expressions.
Today only, get \int_0^1 e^x dx off your purchase!
Donnie
@Donnie: I'll wait for the sales tax holiday. It's been tough since they raised it to π√7 percent.
dan04
+1  A: 

I try to avoid floating-point whenever possible. Nothing irritates me more than having .25 not equal to .25, something that happens when you start dealing with them.

Joe Enos
Except in binary, where 0.25 really is 0.25. 0.1 is a better example :)
Greg Hewgill
Sorry, didn't have a true example off the top of my head.
Joe Enos
Floating-point was designed to be used in scientific applications where the numbers are *inexact* physical measurements, with calculations using *approximations* of functions like `log` and `sin`. Thus, the fact that 0.1 is really 0.1000000000000000055511151231257827021181583404541015625 is not a problem: It's accurate to 16 significant digits.
dan04
@dan04: Mathematical accuracy and real-world accuracy are two different things. Consider the classic 1.1 to 1.5 by .1 example, where 1.5 is actually less than 1.1 + 0.1 + 0.1 + 0.1 + 0.1. In a discount, like the example by the OP, there may be business logic that says something like "Drop the price by 10% each week until it's 50% off the original price". In a purely decimal environment, that's easy. In a floating-point environment, you may or may not be confident in your logic. (I think in this case, it may work, but you see my point).
Joe Enos
A: 

A regular float should be fine, unless you need accuracy to like, five decimal places.

DeadMG
The "regular" floating-point type is `double`, not `float`. The `System.Math` methods all take `double`, not `float`. And most floating-point hardware (including x87) does all arithmetic in double-precision or higher, so there's no performance advantage to `float`. `float` is only useful for saving memory when you have an array of millions of numbers.
dan04
+6  A: 

Floating-point types (float and double are particularly ill-suited to financial applications.

Financial calculations are almost always decimal, while floating-point types are almost always binary. Many common values that are easy to represent in decimal are impossible to represent in binary. For example, 0.2d = 0.00110011...b. See http://en.wikipedia.org/wiki/Binary_numeral_system#Fractions_in_binary for a good discussion.

It's also worth talking about how you're representing prices in your system. decimal is a good choice, but floating point is not, for reasons listed above. Because you believe in Object Oriented Programming, you're going to wrap that decimal in a new Money type, right? A nice treatment of money comes in Kent Beck's Test Driven Development by Example.

Perhaps you will consider representing percentages as an integer, and then dividing by 100 every time you use it. However, you are setting yourself up for bugs (oops, I forgot to divide) and future inflexibility (customer wants 1/10ths of a percent, so go fix every /100 to be /1000. Oops, missed one - bug.)

That leaves you with two good options, depending on your needs. One is decimal. It's great for whole percentages like 10%, but not for things like "1/3rd off today only!", as 1/3 doesn't represent exactly in decimal. You'd like it if buying 3 of something at 1/3rd off comes out as a whole number, right?

Another is to use a Fraction type, which stores an integer numerator and denominator. This allows you to represent exact values for all rational numbers. Either implement your own Fraction type or pick one up from a library (search the internet).

Jay Bazuzi