7

I want to understand how the Java double type will store its value in memory in Java. When I run the following code I get unexpected output:

public static void main(String[] args)  {

    float a = 1.5f;
    float b= 0.5f;
    double c= 1.5;
    double d = 0.5;

    float a1 = 1.4f;
    float b1= 0.5f;
    double c1= 1.4;
    double d1 = 0.5;

    System.out.println(" a- b is " + (a-b));
    System.out.println(" c- d is " + (c-d));
    System.out.println("a1-b1 is " + (a1-b1));
    System.out.println("c1-d1 is " + (c1-d1));

}

Output:

 a- b is 1.0
 c- d is 1.0
a1-b1 is 0.9
c1-d1 is 0.8999999999999999

Why is c1-d1 not equal to 0.9?

I also tried other different values but some time it return expected result and some time not.

Duncan Jones
  • 67,400
  • 29
  • 193
  • 254
MilanPanchal
  • 2,943
  • 1
  • 19
  • 37
  • 4
    Similar to http://stackoverflow.com/questions/322749/retain-precision-with-doubles-in-java – tjg184 Oct 26 '12 at 14:18
  • 1
    This may help http://epramono.blogspot.com/2005/01/double-vs-bigdecimal.html – kosa Oct 26 '12 at 14:18
  • See also http://mindprod.com/jgloss/floatingpoint.html – Duncan Jones Oct 26 '12 at 14:19
  • The rounding errors producing by converting the numeral “1.4” to float or double are insufficient to explain the observed behavior. Both “1.4f” and “1.4” yield values that are not exactly 1.4, and the float value is of course farther from 1.4. When .5 is subtracted, the float remains farther from the exact value (.9), yet printing it yields the exact value. The cause for this is likely in the formatting, not in the floating-point arithmetic. – Eric Postpischil Oct 26 '12 at 14:25
  • 4
    [What every computer scientist should know about floating-point arithmetic](http://floating-point-gui.de/) – Mark Peters Oct 26 '12 at 14:33
  • The ubiquitous computer science tutorial question encountered within the first week of classes. – Blessed Geek Oct 26 '12 at 14:41
  • The vote to close is inappropriate as the behavior of Java’s `println` with floating point is a key part of the observed behavior in this question, and that behavior is not addressed in other questions that I see when searching for “Java println floating-point”. – Eric Postpischil Oct 26 '12 at 15:13

2 Answers2

3

While you might have heard about rounding errors, you might be wondering why you have a rounding error here.

float a1 = 1.4f;
float b1 = 0.5f;
double c1 = 1.4;
double d1 = 0.5;

System.out.println(new BigDecimal(a1) + " - " + new BigDecimal(b1) + " is " +
        new BigDecimal(a1).subtract(new BigDecimal(b1)) + " or as a float is " + (a1 - b1));
System.out.println(new BigDecimal(c1) + " - " + new BigDecimal(d1) + " is " +
        new BigDecimal(c1).subtract(new BigDecimal(d1)) + " or as a double is " + (c1 - d1));

prints

1.39999997615814208984375 - 0.5 is 0.89999997615814208984375 or as a float is 0.9
1.399999999999999911182158029987476766109466552734375 - 0.5 is
     0.899999999999999911182158029987476766109466552734375 
     or as a double is 0.8999999999999999

As you can see, neither float nor double can represent these values exactly, and when the float or double is printed, some rounding occurs to hide this from you. In this case of float, the rounding to 7 decimal places yields the number you expected. In the case of double which has 16 digits of precision, the rounding error is visible.

As @Eric Postpischil, notes whether the float or double operation has a rounding error depends entirely on the values used. In this situation, it was the float which appeared to be more accurate even through the represented value was further from 0.9 than the double value.

In short: if you are going to use float or double you should use a sensible rounding strategy. If you can't do this, use BigDecimal.

System.out.printf("a1 - b1 is %.2f%n", (a1 - b1));
System.out.printf("c1 - d1 is %.2f%n", (c1 - d1));

prints

a1 - b1 is 0.90
c1 - d1 is 0.90

When you print a float or double, it assumes that the nearest short decimal value is the one you really want. i.e. within 0.5 ulp.

E.g.

double d = 1.4 - 0.5;
float f = d;
System.out.println("d = " + d + " f = " + f);

prints

d = 0.8999999999999999 f = 0.9
Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
  • 2
    The sentence “In the case of double which has 16 digits of precision, the rounding error is visible” suggests that the double value was displayed with more digits because double has greater precision. That is not the case. For each type, float and double, whether the result of subtracting .5 is also the representable value nearest the exact result (when the original numerals are used) is effectively happenstance. It is a function of what bits happen to follow the position where rounding occurs, not a function of how many bits are used. For example, try “1.2f - .5f” and “1.2 - .5”. – Eric Postpischil Oct 26 '12 at 15:11
  • @EricPostpischil Thank you. As it was written, it wasn't clear what I meant. – Peter Lawrey Oct 26 '12 at 15:22
2

Java documentation for println refers (through several links) to the documentation for toString. The documentation for toString says that the number of digits printed for float or double is the number needed to uniquely distinguish the value from adjacent representable values in the same type.

When “1.4f” is converted to float, the result is 1.39999997615814208984375 (in hexadecimal floating point, 0x1.666666p+0). When .5 is subtracted, the result is 0.89999997615814208984375 (0x1.ccccccp-1). As it happens, this float is also the float that is closest to .9. So, when it is printed, “.9” is printed.

When “1.4” is converted to double, the result is 1.399999999999999911182158029987476766109466552734375 (0x1.6666666666666p+0). When .5 is subtracted, the result is 0.899999999999999911182158029987476766109466552734375 (0x1.cccccccccccccp-1). This is not the double that is closest to .9, as 0.90000000000000002220446049250313080847263336181640625 (0x1.ccccccccccccdp-1) is closer. Therefore, when it is printed, the Java specification requires that the value be printed more finely, distinguishing it from .9. The result, “0.8999999999999999”, accurately represents the actual value.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312