Java DecimalFormat在格式化double时丢失精度

时间:2015-04-16 23:26:27

标签: java double bigdecimal double-precision decimalformat

当我执行以下代码时:

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);
    }
}

这正确打印-1,350,825,904,190,559,999,913,623,552。 我的代码确实经历了很多双打,因此我不希望从double转换为bigdecimal。我认为BigDecimal的处理时间很长。 我做format.format(value, buffer, position) 我看到精度丢失了。 我得到的输出是-1,350,825,904,190,560,000,000,000,000

我在这里做错了什么?有没有更好的方法来处理这个并仍然保持精度。我不想在这里处理BigDecimals,只是使用小数。

有什么建议吗?

4 个答案:

答案 0 :(得分:2)

double没有无限的精确度,通过将double转换为double,您无法获得比BigDecimal更高的精确度(就像您可以当intdouble r = 1/3; 0.0因为加宽 intdouble时,String会获得更高的精确度)。相反,您可以使用DecimalFormat format = new DecimalFormat(); String value = "-1350825904190559999913623552.00"; System.out.println(format.format(new BigDecimal(value))); 。像

这样的东西
{{1}}

答案 1 :(得分:1)

格式化过程中不会丢失。它在这里丢失了:

Double value = new Double(-1350825904190559999913623552.00);

A double只有大约15.9个有效十进制数字。它不合适。转换浮点文字时,编译时出现精度损失。

答案 2 :(得分:1)

问题在于输出格式,特别是默认情况下如何将双精度转换为字符串。每个双精度数都有一个精确值,但它也是一系列小数部分的字符串到双精度转换的结果。在这种情况下,double的确切值是-1350825904190559999913623552,但范围是[-1350825904190560137352577024,-1350825904190559862474670080]。

Double toString转换从该范围中选择最少有效数字的数字,-1.35082590419056E27。该字符串确实转换回原始值。

如果你真的想要看到确切的值,而不仅仅是足够的数字来唯一地标识双重,那么你当前的BigDecimal方法效果很好。

以下是我用来计算答案中数字的程序:

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)));
  }
}

输出:

Exact value: -1350825904190559999913623552
Range: [-1350825904190560137352577024,-1350825904190559862474670080]
toString result: -1.35082590419056E27
exact value of toString result as double: -1350825904190559999913623552

答案 3 :(得分:0)

您无法使用Double准确代表1350825904190559999913623552.00。如果您想知道原因,请浏览article

如果您想表示该值,我建议您使用您在问题中使用的代码:new BigDecimal( value ),其中value实际上是String代表。