I was reading an article related to difference between Float and double. and they given an example as below :
Say you have a $100 item, and you give a 10% discount. Your prices are all in full dollars, so you use int variables to store prices. Here is what you get:
int fullPrice = 100;
float discount = 0.1F;
Int32 finalPrice = (int)(fullPrice * (1-discount));
Console.WriteLine("The discounted price is ${0}.", finalPrice);
Guess what: the final price is $89, not the expected $90. Your customers will be happy, but you won't. You've given them an extra 1% discount.
In above example, to calculate the final price they have used fullPrice * (1-discount)
. why they used (1-disocunt)
? it should be fullPrice * discount
.
so my confusion is about logic to calculate the final price. why thay used (1-discount)
instead of discount
?
the final price is $89, not the expected $90
That's because when 0.1
is float
, it is not exactly 0.1
, it's a little more. When you subtract it from 1
to do the math, you get $89.9999998509884
. Casting to int
truncates the result to 89
(demo).
You can make this work by using decimal
data type for your discount. This type can represent 0.1 without a precision loss (demo).
why they used (1-disocunt)
1
represents 100%. The price after discount is (100%-10%)=90%
This question is actually about math.
Let's suggest you have an item which costs 100$.
The seller provides a discount to you - 10%.
Now you need to calculate what is the final price of an item.
I.e., how much money should you give to get it?
The answer: you need to pay the full price of an item minus discounted price.
100% of a cost - 10% discount = 90% final price
That's why it is fullPrice * (1 - discount)
.
If you calculate it using your formula fullPrice * discount
then it will mean that the item which costs 100$ will be sold for 10$ due to 10% discount - which is incorrect. Actually, this formula fullPrice * discount
may be used for calculation of discounted amount.
There is nothing wrong with the overall logic of the above example, but it does have very unfortunate choice of data types. This leads to the values being converted implicitly to double, introducing a slight rounding error in the process. By casting back to int, the result is truncated. This greatly amplifies the rounding error.
This is a good example of a problem that frequently occurs when dealing with financial values in programming:
Float values do not tanslate well to decimal fractions.
Most new developers tend to think of float values as decimal fractions, because they are mostly represented by these, when converting them to strings and vice versa. This is not the case. Float values have their fractional part stored as binary fraction, as descibed here.
This makes float values (and their calulations) being slightly askew from their decimal representations. This is why the following results to $89,9999998509884:
double fullPrice = 100;
double discount = 0.1F;
double finalPrice = (fullPrice * (1 - discount));
Console.WriteLine("The discounted price is ${0}.", finalPrice);
(Not so) fun fact: The above will work fine when using float
as data type, because the afforementioned error lies below the resolution of single precision values in this example and the assembly code does use double precision behind the scenes. When the result gets converted to single precision, the error gets lost.
One way out of this problem, is to use the data type decimal
, that was construced to do calculations that have to translate directly to decimal fractions (as financial calculations do):
decimal fullPrice = 100;
decimal discount = 0.1m;
decimal finalPrice = (fullPrice * (1 - discount));
Console.WriteLine("The discounted price is ${0}.", finalPrice);
Another would be to round all results of floating point calculations, before displaying or storing them. For financial calculations one shoud use:
Math.Round([your value here], 2, MidpointRounding.AwayFromZero);