-1

I have seen a very weird behaviour in Java's double variable, as I'm trying to simply add small fractions to a double and I see a completely bizarre results.

double test = 0;
test += 0.71;
test += 0.2;

Now I'd expect the result to be:

test = 0.91

Right? Wrong!

In reality, this is the number I get in my test double:

test = 0.9099999999999999

Now while this is very close, it's a very bizarre fraction loss, and in the long run it causes serious bugs in my program.

With a float I've gotten even a weirder result.

Any help would be greatly appreciated.

Thanks

Ron
  • 1,806
  • 3
  • 18
  • 31
  • possible duplicate of [Moving decimal places over in a double](http://stackoverflow.com/questions/4937402/moving-decimal-places-over-in-a-double) – assylias Apr 25 '12 at 10:10

4 Answers4

4

There is nothing bizarre about it at all. 0.91, 0.71 and 0.2 are not representable as a IEEE754 floating point values as they would have a recurring fractional part when represented in binary. The situation is entirely analogous to trying to represent 1/3 in base 10 with a finite number of digits. You can't do it.

What you are seeing is a rounding error that is normal when doing floating point calculations. You have to code around it. So for instance, you can't reliably compare for equality, you have to see the two numbers are within some small delta of each other. For a slightly more in depth but still understandable explanation see The Floating Point Guide.

JeremyP
  • 84,577
  • 15
  • 123
  • 161
  • That's a great link - to the OP I'd suggest poking around that site for a bit. Lots to learn there, and really concisely presented. – Benjamin Cox Apr 25 '12 at 10:25
1

That's the magic of binary encoding of floating point values (look for IEEE754 : http://en.wikipedia.org/wiki/IEEE_754-2008 ). If you want to be sure to never have this kind of things, you're maybe looking for BigDecimal :

http://docs.oracle.com/javase/1.5.0/docs/api/java/math/BigDecimal.html

Basic rules :

  • don't use equality tests when dealing with floating point numbers (you must test gaps)
  • round numbers you're displaying (usually using DecimalFormat)
  • don't use floating point numbers for financial applications
  • the float is generally the way to go for scientific or industrial operations, as long as you understand IEEE754
Denys Séguret
  • 372,613
  • 87
  • 782
  • 758
  • So how would you solve the simple test I did to receive 0.92 using a BigDecimal? – Ron Apr 25 '12 at 10:26
  • Just to add to dystroy's reply; internally binary numbers are represented as n,..., 128,64,32,16,8,4,2, 0 ,1/2,1/4,1/8,...,1/n. – SteJav Apr 25 '12 at 10:52
  • Ron, the solution depends on your exact use case. Most of the times you don't need BigDecimal. If you want to check if numbers are equal, simply test abs(a-b) – Denys Séguret Apr 25 '12 at 11:14
1

double can only approximate most fractional values. This means you need to use some rounding if you want to get your expect result. Or you can use BigDecimal which takes care of this issue for you.

double test = 0;
test += 0.71;
test += 0.2;
System.out.printf("%.2f%n", test);

prints

0.91

For your own interest

System.out.println("0.71 is actually " + new BigDecimal(0.71));
System.out.println("0.2 is actually " + new BigDecimal(0.2));
System.out.println("0.71+0.2 is actually " + new BigDecimal(0.71 + 0.2));
System.out.println("0.91 is actually " + new BigDecimal(0.91));
System.out.println("0.71+0.2 == 0.91 is " + (0.71 + 0.2 == 0.91));

prints

0.71 is actually 0.70999999999999996447286321199499070644378662109375
0.2 is actually 0.200000000000000011102230246251565404236316680908203125
0.71+0.2 is actually 0.9099999999999999200639422269887290894985198974609375
0.91 is actually 0.91000000000000003108624468950438313186168670654296875
0.71+0.2 == 0.91 is false
Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
  • Printing the number isn't good enough, I'm counting on the "correct" value to come so that my code works as expected. – Ron Apr 25 '12 at 10:28
0

Java uses something called floating-point to represent decimals. They use exponential notation. Here's what I mean:

There is a multiplier (M), and an exponent between 1023 and -1022 (E).

A number (N) is represented like this: M * 2^E.

4.25 is represented like this:

17 * 2^-2.

0.91 cannot be represented in base 2 exactly, but Java can get pretty close:

0.909999999999..

Therefore, it is impossible to accurately add these numbers together.

codecubed
  • 780
  • 7
  • 8