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

Java DecimalFormat在格式化双精度时丢失精度,java,double,bigdecimal,double-precision,decimalformat,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();

当我执行以下代码时:

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);
    }
}
这将正确打印
-1350825904190559999913623552
。 我有一个代码,它确实经历了很多双精度运算,所以我不希望从双精度到大十进制的转换。我认为BigDecimal的处理时间很长。 所以我做
format.format(值、缓冲区、位置)
我发现精度已经降低了。 我得到的输出是
-1350825904190560000000000000

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


有什么建议吗?

double
没有无限的精度,通过将
double
转换为
BigDecimal
您无法获得比
double
更高的精度(比如当你使用
double r=1/3;
时,你不能用
int
获得更高的精度,这是
0.0
,因为它将
int
扩展为
double
)。相反,你可以使用
字符串

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

您无法使用
双精度
准确表示13508259041905599913623552.00。如果您想知道原因,请探究此问题


如果您想表示值,我建议使用您在问题中使用的代码:
new BigDecimal(value)
,其中
value
实际上是一个
字符串
表示形式。

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

Double value = new Double(-1350825904190559999913623552.00);

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

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

Double-toString转换从该范围中选取有效位数最少的数字-1.35082590419056E27。该字符串会转换回原始值

如果您真的想看到准确的值,而不仅仅是足够的数字来唯一地标识double,那么您当前的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

也许发布提供错误结果的代码也是有意义的?你确定这真的很重要吗?如果你有
Double value=new Double(0.1)
,你想看到完整的精度吗(即
0.10000000000000055115123125782702118158340451015625
)?@user2357112嗯,我这里说的是像1350825904190559999913623552.00这样的大数字。所以是的,这很重要。@Lashane代码中唯一的变化不是转换成bigdecimal。我把它粘贴到了code@AnushaPachunuri:如果你想从一个双精度中得到超过15位的精度,你就需要比双精度更高的精度。我想要么你需要BigDecimal,要么你不需要保持完整的精度。2^53和Double.MAX_值之间的一些(但不是全部)数字是完全可以表示的,在我看来,这似乎是其中之一。我熟悉该网页,其中没有任何内容与某些大数字完全可以表示的说法相矛盾,而这一点是其中之一。你可以用@Patricia来检查它-所以你是说这个数字可以表示为一个双精度数字,而不仅仅是一个双精度数字,但是在格式化它的时候。对吗?我喜欢你的解释。不过这个程序对我来说有点费劲。所以我有两个问题:1)这个范围是多少[-1350825904190560137352577024,-1350825904190559862474670080]2)从你所说的,在这种情况下,格式化双精度只能显示足以从范围中唯一标识它的数字?即使双精度在2^53和double.MAX_值的范围内,格式化也不能容纳所有正确的数字?对不起,我想我理解了!这是一个很好的解释。我对对于指定的小数范围,字符串到双精度的转换是相同的。有更好的链接可以阅读吗?JLS参考java.lang.double的API文档来进行字符串到双精度的转换。特别是“然后从概念上将这个精确的数值转换为”无限精确的数值”“然后按照IEEE 754浮点运算的常用四舍五入至最接近规则将二进制值四舍五入为double类型”。但我认为精度仅在格式化过程中丢失。如果在创建double时丢失了精度,为什么转换为bigdecimal会保留它。因此,它不会与”通过将一个double转换为BigDecimal,你无法获得比double更高的精度,是吗?你说了很多double。0.1+0.2是什么?