I have two Double value but different decimal places.
Doubles don't store that information. In fact, they don't even store decimals. Computers are binary after all. Here's a secret:
double d = 0.1;
That line, is a total lie. It does not store 0.1 in d
. That is because it is impossible to store this number.
Before that makes you go: What.. the? Think about it: I divide 1 by 3 and ask you to write that down on a piece of paper. So, you start, 0.33333333.... and where do you end? See? As a human using decimal, you cannot store this number on a piece of paper in decimal format.
Computers don't do decimal, they do binary. They can store 0.5
perfectly. 0.25
is no problem either. But 0.1
? No, no can do. a tenth works out in decimal really nice, but in binary it doesn't (nothing does, except factors of 2, obviously). So, where in decimal you can write '1/10' on a piece of paper perfectly, but you cannot write '1/3' on a piece of paper perfectly, computers can only write X/Y where Y is a factor of 2 perfectly. 1/65536 - no problem. 1/10? Nope.
So why does the above compile? Because double/float math will round to the nearest representable number, and each mathematical operation is similarly rounded. Everything rounds, everywhere, at all times, silently, with no way to know how much error this rounding introduces.
Let's see it in action!
double d = 0;
for (int i = 0; i < 8; i++) d += 0.1;
double e = 0.8;
System.out.println(d == e); // prints... f... false? What?
System.out.println(e); // prints "0.8"
System.out.println(d); // prints "0.7999999999999999"
Thus, when talking about 'formatting' a double in the sense of 'I want x digits', this is completely misleading, because the thing that is in that variable has digits written out in binary and not in decimal, and is rounded to something that doesn't even make sense in decimal.
The simple upshot is that all doubles are slightly wrong and if you are writing systems or applications where this slight error is not acceptable (trivial example: Everything financial - you do NOT want to lose a random cent due to impossible to predict rounding errors!!), then you cannot use double
.
There are 2 broad solutions to this problem:
- Use atoms, in
int
or long
.
For example, do not store an item that costs a buck and a half as double price = 1.50
. Store it as int price = 150;
- in other words, find the atomary (smallest hard/impossible to divide unit) for whatever it is you are attempting to store perfectly and store that. Given that there is such a thing as an atomary, when asked to divide you're hosed anyway and you need to think about it, there is no one set way to do it. For example, if I am a bank and I need to levy a transaction charge of €100,- to a partnership of 3 companies that are equal partners, then what do I do? Charge each €33,34? Charge each €33,33 and eat the cent? Roll some dice, charge one partner at random €33,34, and the other two €33,33? Demand that the parties involved appoint a main account, which gets charged all of it, or if not, the €33,34? Track in each account a residual charge of 0.33333333333333333333333 cents (tossing the remainder away as a rounding error that truly nobody is ever going to care about), even though generally in financial systems, you cannot convey the notion of fractional cents?
There is no right or wrong answer to this question, which means, a / b
cannot possibly work here - how could that ever know which of those 5 different things you wanted?
- Use the
java.math.BigDecimal
class, which stores in decimal, and stores perfectly. But note that if you ask BigDecimal to divide 1 by 3, it throws an exception, because that is not possible, so, apply caution.