4

When i execute the below code:

public class Test {
    public static void main(String args[]){
        DecimalFormat format = new DecimalFormat();
        Double value = new Double(-1350825904190559999913623552.00);

        StringBuffer buffer = new StringBuffer();
        FieldPosition position = new FieldPosition(0);
        format.format(new BigDecimal(value), buffer, position);
        System.out.println(buffer);
    }
}

This correctly prints -1,350,825,904,190,559,999,913,623,552. I have code which does go through a lot of doubles so I dont want the conversion from double to bigdecimal. I figured the processing time for BigDecimal is large. So i do format.format(value, buffer, position) And i see the precision is lost. The output I get is -1,350,825,904,190,560,000,000,000,000.

What am i doing wrong here? Is there a better way to deal with this and still retain the precision. I don't want to deal with BigDecimals here but just work with the decimals.

Any suggestions?

Anusha Pachunuri
  • 1,389
  • 4
  • 18
  • 39
  • maybe it makes sense to post code which provides incorrect results too? – Iłya Bursov Apr 16 '15 at 23:30
  • Are you sure this actually matters? If you had `Double value = new Double(0.1)`, would you want to see the full precision (which is `0.1000000000000000055511151231257827021181583404541015625`)? – user2357112 Apr 16 '15 at 23:31
  • @user2357112 umm im talking about large numbers like 1350825904190559999913623552.00 here. So yes it does matter. – Anusha Pachunuri Apr 16 '15 at 23:34
  • @Lashane the only change in code is not converting to bigdecimal. i pasted it in the code – Anusha Pachunuri Apr 16 '15 at 23:34
  • @AnushaPachunuri: If you want more than about 15 digits of precision out of a double, you want more than a double can actually do for you. I think either you need BigDecimal, or you don't need to keep the full precision. – user2357112 Apr 16 '15 at 23:36

4 Answers4

2

double doesn't have infinite precision, and you can't gain more precision than a double has by converting a double to a BigDecimal (like you can't gain more precision with an int when you do double r = 1/3; which is 0.0 because it widens an int to a double). Instead, you could use a String. Something like

DecimalFormat format = new DecimalFormat();
String value = "-1350825904190559999913623552.00";
System.out.println(format.format(new BigDecimal(value)));
Elliott Frisch
  • 198,278
  • 20
  • 158
  • 249
  • But i think the precision is only lost during formatting. If the precision was lost while creating the double, why is converting to bigdecimal retaining it. So it doesnt fall in the same case as "you can't gain more precision than a double has by converting a double to a BigDecimal", does it? – Anusha Pachunuri Apr 17 '15 at 18:17
  • You said a "lot of doubles". What's 0.1+0.2? – Elliott Frisch Apr 17 '15 at 18:28
2

The issue is in the output formatting, specifically how doubles are converted to strings by default. Each double number has an exact value, but it is also the result of string to double conversion for a range of decimal fractions. In this case, the exact value of the double is -1350825904190559999913623552, but the range is [-1350825904190560137352577024,-1350825904190559862474670080].

The Double toString conversion picks the number from that range with the fewest significant digits, -1.35082590419056E27. That string does convert back to the original value.

If you really want to see the exact value, not just enough digits to uniquely identify the double, your current BigDecimal approach works well.

Here is the program I used to calculate the numbers in this answer:

import java.math.BigDecimal;

public class Test {
  public static void main(String args[]) {
    double value = -1350825904190559999913623552.00;
    /* Get an exact printout of the double by conversion to BigDecimal
     * followed by BigDecimal output. Both those operations are exact.
     */
    BigDecimal bdValue = new BigDecimal(value);
    System.out.println("Exact value: " + bdValue);
    /* Determine whether the range is open or closed. The half way
     * points round to even, so they are included in the range for a number
     * with an even significand, but not for one with an odd significand.
     */
    boolean isEven = (Double.doubleToLongBits(value) & 1) == 0;
    /* Find the lower bound of the range, by taking the mean, in
     * BigDecimal arithmetic for exactness, of the value and the next
     * exactly representable value in the negative infinity direction.
     */
    BigDecimal nextDown = new BigDecimal(Math.nextAfter(value,
        Double.NEGATIVE_INFINITY));
    BigDecimal lowerBound = bdValue.add(nextDown).divide(BigDecimal.valueOf(2));
    /* Similarly, find the upper bound of the range by going in the
     * positive infinity direction.
     */
    BigDecimal nextUp = new BigDecimal(Math.nextAfter(value,
        Double.POSITIVE_INFINITY));
    BigDecimal upperBound = bdValue.add(nextUp).divide(BigDecimal.valueOf(2));
    /* Output the range, with [] if closed, () if open.*/
    System.out.println("Range: " + (isEven ? "[" : "(") + lowerBound + ","
        + upperBound + (isEven ? "]" : ")"));
    /* Output the result of applying Double's toString to the value.*/
    String valueString = Double.toString(value);
    System.out.println("toString result: " + valueString);
    /* And use BigDecimal as above to print the exact value of the result
     * of converting the toString result back again.
     */
    System.out.println("exact value of toString result as double: "
        + new BigDecimal(Double.parseDouble(valueString)));
  }
}

Output:

Exact value: -1350825904190559999913623552
Range: [-1350825904190560137352577024,-1350825904190559862474670080]
toString result: -1.35082590419056E27
exact value of toString result as double: -1350825904190559999913623552
Patricia Shanahan
  • 25,849
  • 4
  • 38
  • 75
  • I like your explanation. the program is a little consufing for me though.So two questions i have is 1) what is this range [-1350825904190560137352577024,-1350825904190559862474670080] 2)From what you say,formatting the double in such a case is only showing the digits enough to uniquely identify it from the range? even though the double is in the range of 2^53 and Double.MAX_VALUE , the formatting wont accomodate all the digits correct? – Anusha Pachunuri Apr 17 '15 at 18:14
  • Sorry , i think i understood it! its a great explanation. im intrigued by the fact that the string to double conversions for the range of decimalfractions you specified is the same. Any better links to read about this? – Anusha Pachunuri Apr 17 '15 at 18:36
  • The JLS refers to the API documentation for java.lang.Double [valueOf](https://docs.oracle.com/javase/8/docs/api/java/lang/Double.html#valueOf-java.lang.String-) for the string to double conversion. In particular "this exact numerical value is then conceptually converted to an "infinitely precise" binary value that is then rounded to type double by the usual round-to-nearest rule of IEEE 754 floating-point arithmetic". – Patricia Shanahan Apr 17 '15 at 23:22
1

It isn't lost during formatting. It is lost right here:

Double value = new Double(-1350825904190559999913623552.00);

A double only has about 15.9 significant decimal digits. It doesn't fit. There was a precision loss at compile time when the floating-point literal was converted.

user207421
  • 305,947
  • 44
  • 307
  • 483
0

You cannot represent 1350825904190559999913623552.00 accurately with a Double. If you would like to know why, explore this article.

Should you want to represent the value, I would advise using the code you have used in your question: new BigDecimal( value ), where value is actually a String representation.

Zyn
  • 614
  • 3
  • 10
  • Some, but not all, numbers between 2^53 and Double.MAX_VALUE are exactly representable, and this seems to me to be one of them. – Patricia Shanahan Apr 17 '15 at 00:01
  • http://en.wikipedia.org/wiki/Floating_point#IEEE_754%3a_floating_point_in_modern_computers – Zyn Apr 17 '15 at 00:24
  • 1
    I am familiar with that web page, and there is nothing in it that contradicts the claim that some large numbers are exactly representable, and this is one of them. You can check it using a [Decimal to Floating-Point Converter](http://www.exploringbinary.com/floating-point-converter/) – Patricia Shanahan Apr 17 '15 at 01:39
  • @Patricia - so you're saying this number is representable as a double and not losing its precision just being a double but when formatting it. correct? – Anusha Pachunuri Apr 17 '15 at 18:20