Java DecimalFormat loses precision when formatting double

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 prints correctly -1,350,825,904,190,559,999,913,623,552

. I have code that goes through a lot of pairs, so I dont want the conversion from double to bigdecimal. I realized that BigDecimal processing time is long. So I do format.format(value, buffer, position)

And I see that 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 handle this and keep it accurate. I don't want to deal with BigDecimals, but just work with decimals.

Any suggestions?

+3


source to share


4 answers


double

does not have infinite precision, and you cannot get more precision than double

that by converting double

to BigDecimal

(for example, you cannot get more precision with int

when you execute double r = 1/3;

which 0.0

, because it expands int

to double

). You can use instead String

. Something like



DecimalFormat format = new DecimalFormat();
String value = "-1350825904190559999913623552.00";
System.out.println(format.format(new BigDecimal(value)));

      

+2


source


It is not lost during formatting. It's lost right here:

Double value = new Double(-1350825904190559999913623552.00);

      



A double

only has about 15.9 significant decimal digits. This does not fit. There was a precision loss at compile time when the floating point literal was converted.

+1


source


The problem is with the formatting of the output, specifically about how the default is doubled. Each double has a precise meaning, but it is also the result of converting the string to double conversion for a range of decimal fractions. In this case, the exact value for the double is -1350825904190559999913623552, but the range is [-1350825904190560137352577024, -1350825904190559862474670080].

The Double toString conversion selects a number from this range with the least significant digits, -1.35082590419056E27. This string converts back to the original value.

If you really want to see the exact value, not just numbers, to uniquely identify the double, your current BigDecimal method 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 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

      

+1


source


You cannot accurately represent 1350825904190559999913623552.00 with Double

. If you want to know why, read the article.

If you want to represent this value, I would suggest using the code you used in your question: new BigDecimal( value )

where value

is the actual representation String

.

0


source







All Articles