20

If I have an array of doubles that each have EXACTLY two decimal places, add them up altogether via a loop, and print out the total, what comes out is a number with MORE THAN two decimal places. Which is weird, because theoretically, adding two numbers that each have 2 and only 2 decimal places will NEVER produce a number that has a non-zero digit beyond the hundredths place.

Try executing this code:

double[] d = new double[2000];
for (int i = 0; i < d.length; i++) {
    d[i] = 9.99;
}

double total = 0,00;
for (int i = 0; i < d.length; i++) {
    total += d[i];
    if (("" + total).matches("[0-9]+\\.[0-9]{3,}")) { // if there are 3 or more decimal places in the total
        System.out.println("total: " + total + ", " + i); // print the total and the iteration when it occured
    }
}

In my computer, this prints out:

total: 59.940000000000005, 5

If I round off the total to two decimal places then I'd get the same number as I would if I manually added 9.99 six times on a calculator. But how come this is happening and where are the extra decimal places coming from? Am I doing something wrong or (I doubt this is likely) is this a Java bug?

Matthew Quiros
  • 13,385
  • 12
  • 87
  • 132
  • possible duplicate of [Unpredictable double](http://stackoverflow.com/questions/9667324/unpredictable-double) – assylias Mar 28 '12 at 11:10
  • Have you tried using DecimalFormat and RoundingMode? http://docs.oracle.com/javase/6/docs/api/java/text/DecimalFormat.html http://docs.oracle.com/javase/6/docs/api/java/math/RoundingMode.html – ChadNC Mar 28 '12 at 11:11

6 Answers6

16

Are you familiar with base 10 to base 2 conversion (decimal to binary) for fractions? If not, look it up.

Then you'll see that although 9.99 looks pretty normal in base 10, it doesn't really look that nice in binary; It looks like a repeating decimal, but in binary. I'm sure you've seen a repeating decimal before, right? It doesn't end. But Java (or any language for that matter) has to save that infinite sequence of digits into a limited number of bytes. And that's when the extra digits appear. When you convert that truncated binary back to decimal, you're really dealing with a different number. The number stored in the variable isn't 9.99 exactly, it something like 9.9999999991 (just an example, I didn't work out the math).

But you're probably interested on how to solve this, right? Look up the BigDecimal class. That's what you want to use for your calculations, especially when dealing with currency. Also, look up DecimalFormat, which is a class for writing a number as a properly formatted string. I think it does rounding for you when you want to show only 2 decimal digits and your number has a lot more, for example.

Andre
  • 3,874
  • 3
  • 35
  • 50
10

If I have an array of doubles that each have EXACTLY two decimal places

Let's stop right there, because I suspect you don't. For example, you give 9.99 in your sample code. That isn't really 9.99. That's "the closest double to 9.99" as 9.99 itself can't be exactly represented in binary floating point.

At that point, the rest of your reasoning goes out of the window.

If you want values with an exact number of decimal digits, you should use a type which stores values in a decimal-centric manner, such as BigDecimal. Alternatively, store everything as integers and "know" that you're actually remembering "the value * 100" instead.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • 3
    +1: floating point represent a number as a series of powers of 2. e.g. 1.625 is 2^0 + 2^-1 + 2^-3. So there are four values with two decimal places 0.00, 0.25, 0.50, 0.75 and these will not have a rounding (for +, -, * and /) or representation error. All other two decimal place values are an approximation. – Peter Lawrey Mar 28 '12 at 11:29
4

Doubles are represented in a binary format on the computer (). This means that certain numbers cannot be represented accurately, so the computer will use the closest number that can be represented.

E.g. 10.5 = 2^3+2+2^(-1) = 1.0101 * 2^3 (here the mantissa is in binary)
but 10.1 = 2^3+2+2^(-4)+2^(-5)+(infinite series here) = 1.0100001... * 2^3

9.99 is such a number with infinite representation. Thus when you add them together, the finite representation used by the computer is used in the calculation and the result will be even more further away from the mathematical sum than the originals were from their true representation. This is why you see more digits displayed than used in the original numbers.

Attila
  • 28,265
  • 3
  • 46
  • 55
3

this is because of floating point arithmetics.

doubles and floats are not exactly real numbers, there are finite number of bits to represent them while there are infinite number of real numbers [in any range], so you cannot represent all real numbers - You are getting the closest number you can have with the floating point representation.

Whenever you deal with floating points - remember that they are only an approximation to the number you are seeking. You might want to use BigDecimal if you want the exact number [or at least control the error].

More info can be found at this article

amit
  • 175,853
  • 27
  • 231
  • 333
3

Use BigDecimal to perform floating point calculations with precision. It's a must when it comes to money.

This is a known issue that stems in the fact that binary calculations don't allow for precise floating point operations. Look at "floating point arithmetics" for more details.

Bozho
  • 588,226
  • 146
  • 1,060
  • 1,140
1

This is due to inaccuracies when it comes to representing decimal numbers using a binary floating point value. In other words, the double literal 0.99 does not actually represent the mathematical value 9.99.

To reveal exactly what number a value, such as 9.99 represents you could let BigDecimal print the value.

Code to reveal the exact value:

System.out.println(new BigDecimal(9.99));

Output:

9.9900000000000002131628207280300557613372802734375

Btw, your reasoning would be completely accurate if you were taking about binary places instead of decimal places, since a number with two binary places can be exactly represented by a binary floating point value.

aioobe
  • 413,195
  • 112
  • 811
  • 826