1

In java following expression results into

new Double(1.0E22) + new Double(3.0E22)  = 4.0E22

but

new Double(1.0E22) + new Double(4.0E22)  = 4.9999999999999996E22

I was expecting it to be 5.0E22. The Double limit is 1.7976931348623157E308. Appreciate your help. My machine's architecture is x64 and JVM is also 64 bit.

TheMerovingian
  • 648
  • 4
  • 14
Naman
  • 2,569
  • 4
  • 27
  • 44
  • Take a look at the answer of this post: http://stackoverflow.com/questions/10786587/java-double-precision-sum-trouble – Mik378 May 08 '13 at 13:25
  • 1) new Double(1.0E22) + new Double(3.0E22) makes no sense since it's equivalent to 1.0E22 + 3.0E22 – Evgeniy Dorofeev May 08 '13 at 13:30
  • The problem is that `5^23` has 54 significant bits, `0x2a5a058fc295ed`, but `double` has only 53. So `5.0e22` gets rounded down to `(5^23 - 1)*2^22`. – Daniel Fischer May 08 '13 at 16:55
  • @DanielFischer but double's max value is 1.7976931348623157E308. So i am safely within that range. Still why am i seeing the rounding off? – Naman May 08 '13 at 17:24
  • Because `double` has only 53 bits of precision, but `5.0e22` needs 54. `double` can only exactly represent numbers that need 53 bits of precision or less. `1.0e22`, `2.0e22`, `3.0e22` and `4.0e22` all need only 52 or 53 (3.0e22) bits of precision to be exactly represented (and the exponent is small enough), so those are exactly represented. – Daniel Fischer May 08 '13 at 17:31
  • 1
    @Mik378 Thanks, BigDecimal worked for me. – Naman May 09 '13 at 05:47

2 Answers2

4

Welcome to the planet of floating point units. Unfortunately, in a real world, you have to give up some precision to get speed and breadth of representation. You cannot avoid that: double is only an approximate representation. Actually, you cannot represent a number but with finite precision. Still, it's a good approximation: less than 0.00000000001% error. This has nothing to do with double upper limits, rather with CPU limits, try doing some more math with Python:

>>> 4.9999999999999996 / 5.
1.0
>>> 5. - 4.9999999999999996
0.0

See? As a side note, never check for equality on double, use approximate equality:

if ((a - b) < EPSILON)

Where EPSILON is a very small value. Probably Java library has something more appropriate, but you get the idea.

If you are insterested in some theory, the standard for floating point operations is IEEE754

Stefano Sanfilippo
  • 32,265
  • 7
  • 79
  • 80
0

There are a few ways that you can reduce floating point errors, e.g. pairwise summation and Kahan summation, but neither of these will help you precisely represent a number like 5.0E22 - as Stefano Sanfilippo stated in his answer, that's due to the limits of what you can represent in using floating point, as opposed to a problem with the algorithm used to achieve the answer. To represent 5.0E22 you should either use BigDecimal or else use a library that has a Rational data type, e.g. JScience.

Zim-Zam O'Pootertoot
  • 17,888
  • 4
  • 41
  • 69