31

My coworker did this experiment:

public class DoubleDemo {

      public static void main(String[] args) {
           double a = 1.435;
           double b = 1.43;
           double c = a - b;
           System.out.println(c);
      }
 }

For this first-grade operation I expected this output:

0.005

But unexpectedly the output was:

0.0050000000000001155

Why does double fails in such a simple operation? And if double is not the datatype for this work, what should I use?

Carlos Gavidia-Calderon
  • 7,145
  • 9
  • 34
  • 59

6 Answers6

30

double is internally stored as a fraction in binary -- like 1/4 + 1/8 + 1/16 + ...

The value 0.005 -- or the value 1.435 -- cannot be stored as an exact fraction in binary, so double cannot store the exact value 0.005, and the subtracted value isn't quite exact.

If you care about precise decimal arithmetic, use BigDecimal.

You may also find this article useful reading.

Louis Wasserman
  • 191,574
  • 25
  • 345
  • 413
  • 1
    I have a question if we have a double and we convert it to BigDecimal before doing the arithmetic operations, will it work? Going to check myself though :) – muasif80 Jun 16 '20 at 13:09
  • 1
    @muasif80: No, it won't. If you want to do precise arithmetic, you mustn't ever touch a `double`. – Louis Wasserman Jun 16 '20 at 15:29
  • Ok thanks. I though tried an example as shared below. But I will try to follow your advice. – muasif80 Jun 16 '20 at 16:08
  • Where this idea of "sum of fractions" comes from? You usually have exponent and mantissa such that the represented value is V=(2^e)*(1.m) – Jack Nov 10 '20 at 15:03
  • @Jack: Yes. What do you think `m` is? – Louis Wasserman Nov 10 '20 at 18:06
  • @LouisWasserman `m` is the number to be used as decimal part of the right factor; something like if `m=123` then the right factor becomes `1.123` (not true, a bit more complex, but to simplify). Can you please share how `(2^e)*(1.m)` can be associated to a sum of fractions? I would rather say that it's a SINGLE power-of-2 fraction (ie: 1/4, but also integer 16, 32 (that's the `2^e` part)), multiplied by a factor `M` such that `1 <= M < 2`. I don't see any series of sums here – Jack Nov 11 '20 at 10:51
  • How is that number 1 <= M < 2 represented? It _is_ a binary fraction: e.g. 1 + 1/4 + 1/16. – Louis Wasserman Nov 11 '20 at 18:01
  • @LouisWasserman now it's clear. Although technically correct, I strongly believe this description is misleading. The fact that a number is stored as sum of `v/b^p` (v=value, b=base, p=position) is true for every number representation, not only double, but also int and long; it is also true for the good old base 10 written by humans. When I write the number `57.23` in fact I'm encoding it as a sum of `5/10^-1+7/10^0+2/10^1+3/10^2`; nevertheless I would never tell anybody that human kind uses to write numbers as a sum of fractions.. well, maybe if an ancient Roman asks about positional system:) – Jack Nov 11 '20 at 22:47
  • To conclude @LouisWasserman, main difference in how double is stored compared to other numerical values is really not in the sum of fractions story. It's like answering "internally it has seats" to someone asking about airplanes. – Jack Nov 11 '20 at 22:49
  • The point is that unlike with integers, fractions that are exact in decimal can't be represented exactly in binary. That's distinctive and important. – Louis Wasserman Nov 11 '20 at 22:54
  • To be more precise some exact decimal fraction can be represented exactly in binary. But this has nothing to do with the fact that the mantissa (actually also exponent, like (almost) every number in the world) is stored as a sum of `1/2+1/4+1/8`.. that is it's stored in binary. It has to do with the fact that the represented value is approximated by the formula `(2^e)*(1.m)`, which of course cannot represent the infinite domain R. This is true even if we choose different format for storing such values, even if a `double` stores `e` and `m` values internally as Strings. Cheers – Jack Nov 11 '20 at 23:23
4

double and float are not exactly real numbers.

There are infinite number of real numbers in any range, but only finite number of bits to represent them! for this reason, rounding errors is expected for double and floats.

The number you get is the closest number possible that can be represented by double in floating point representation.

For more details, you might want to read this article [warning: might be high-level].

You might want to use BigDecimal to get exactly a decimal number [but you will again encounter rounding errors when you try to get 1/3].

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

Yes it worked this way using BigDecimal operations

private static void subtractUsingBigDecimalOperation(double a, double b) {
  BigDecimal c = BigDecimal.valueOf(a).subtract(BigDecimal.valueOf(b));
  System.out.println(c);
}

enter image description here

muasif80
  • 5,586
  • 4
  • 32
  • 45
  • 1
    `BigDecimal.valueOf` will work for some `double` values, but not all. – Louis Wasserman Jun 16 '20 at 16:12
  • Ok. I have to check how the double value comes up in the first place then in my particular case. I will try to alter that according to your insight. Thanks for the reply. – muasif80 Jun 16 '20 at 16:18
2

double and float arithmetic are never going to be exactly correct because of the rounding that occurs "under the hood".

Essentially doubles and floats can have an infinite amount of decimals but in memory they must be represented by some real number of bits. So when you do this decimal arithmetic a rounding procedure occurs and is often off by a very small amount if you take all of the decimals into account.

As suggested earlier, if you need completely exact values then use BigDecimal which stores its values differently. Here's the API

Austin Heerwagen
  • 653
  • 3
  • 12
  • It's not accurate to say they'll "never" be exactly correct. It would be correct to say that they will never be exactly correct when representing anything other than fractions whose denominator is not a power of two. – supercat Feb 15 '15 at 17:36
1
public class BigDecimalExample {

    public static void main(String args[]) throws IOException {

      //floating point calculation
      double amount1 = 2.15;
      double amount2 = 1.10;
      System.out.println("difference between 2.15 and 1.0 using double is: " + (amount1 - amount2));

      //Use BigDecimal for financial calculation
      BigDecimal amount3 = new BigDecimal("2.15");
      BigDecimal amount4 = new BigDecimal("1.10") ;
      System.out.println("difference between 2.15 and 1.0 using BigDecimal is: " + (amount3.subtract(amount4)));       
    }      
}

Output:

difference between 2.15 and 1.0 using double is: 1.0499999999999998 difference between 2.15 and 1.0 using BigDecmial is: 1.05

Suraj Rao
  • 29,388
  • 11
  • 94
  • 103
-2
 //just try to make a quick example to make b to have the same precision as a has, by using BigDecimal

 private double getDesiredPrecision(Double a, Double b){
     String[] splitter = a.toString().split("\\.");
     splitter[0].length();   // Before Decimal Count
     int numDecimals = splitter[1].length(); //After Decimal Count

     BigDecimal bBigDecimal = new BigDecimal(b);
     bBigDecimal = bBigDecimal.setScale(numDecimals,BigDecimal.ROUND_HALF_EVEN);

     return bBigDecimal.doubleValue();  
 }
  • That does not answer the question. – Baum mit Augen Aug 15 '14 at 14:08
  • well, you are right. Just try to make some small example on BigDecimal. There are already people answering the question. No need to be repetitive. – madmaximshen Aug 15 '14 at 15:21
  • On SO, answers are for providing a complete, self contained answer. If you feel something is missing in another answer, leave a comment once you have enough reputation. If you think you can write a better *complete* answer, do that. Do not post "half-answers" like this one. – Baum mit Augen Aug 15 '14 at 15:27
  • this is not good because its just setting values its not working for negative values subtraction – Chandu D Dec 16 '15 at 05:50