Java BigDecimal精度问题

时间:2012-03-20 21:36:30

标签: java bigdecimal double-precision

我知道以下行为是一个老问题,但我仍然不明白。

System.out.println(0.1 + 0.1 + 0.1);    

即使我使用BigDecimal

System.out.println(new BigDecimal(0.1).doubleValue()
    + new BigDecimal(0.1).doubleValue()
    + new BigDecimal(0.1).doubleValue());

为什么这个结果是:0.30000000000000004而不是:0.3

我该如何解决这个问题?

5 个答案:

答案 0 :(得分:27)

你真正想要的是

new BigDecimal("0.1")
 .add(new BigDecimal("0.1"))
 .add(new BigDecimal("0.1"));

new BigDecimal(double)构造函数获得double的所有不精确性,所以当你说0.1时,你已经引入了舍入误差。使用String构造函数可以避免与通过double相关联的舍入错误。

答案 1 :(得分:9)

首先从不,永远不要使用BigDecimal的双构造函数。在少数几种情况下它可能是正确的,但大部分都不是

如果您可以控制输入,请使用已提出的BigDecimal String构造函数。这样你就能得到你想要的东西。如果你已经有一个double(毕竟可能发生),不要使用double构造函数,而是使用静态valueOf方法。这有一个很好的优势,我们得到双重的典型表示,至少可以缓解问题..结果通常更加直观。

答案 2 :(得分:4)

这不是Java的问题,而是一般的计算机问题。核心问题在于从十进制格式(人格式)到二进制格式(计算机格式)的转换。十进制格式的某些数字不能以二进制格式表示,没有无限重复小数。

例如,0.3十进制是0.01001100 ...二进制但是计算机有一个有限的“槽”(位)来保存数字,所以它无法保存所有的无限表示。它只保存 0.01001100110011001100(例如)。但是,十进制数不再是0.3,而是0.30000000000000004。

答案 3 :(得分:3)

试试这个:

BigDecimal sum = new BigDecimal(0.1).add(new BigDecimal(0.1)).add(new BigDecimal(0.1));

编辑:实际上,查看Javadoc,这将与原始问题相同。构造函数BigDecimal(double)将使一个BigDecimal对应于0.1的精确浮点表示,它不完全等于0.1。

然而,这给出了确切的结果,因为整数CAN总是以浮点表示形式表示:

BigDecimal one = new BigDecimal(1);
BigDecimal oneTenth = one.divide(new BigDecimal(10));

BigDecimal sum = oneTenth.add(oneTenth).add(oneTenth);

答案 4 :(得分:3)

你遇到的问题是0.1用稍高的数字表示,例如

System.out.println(new BigDecimal(0.1));

打印

0.1000000000000000055511151231257827021181583404541015625

Double.toString()会考虑此表示错误,因此您无法看到它。

类似地,0.3表示的值略低于实际值。

0.299999999999999988897769753748434595763683319091796875

如果将表示的值乘以0.1乘以3,则不会得到0.3的代表值,而是会得到更高的值

0.3000000000000000166533453693773481063544750213623046875

这不仅是表示错误,还是操作引起的舍入错误。这比Double.toString()更正,所以你看到舍入错误。

故事的寓意,如果你使用floatdouble也适当地围绕解决方案。

double d = 0.1 + 0.1 + 0.1;
System.out.println(d);
double d2 = (long)(d * 1e6 + 0.5) / 1e6; // round to 6 decimal places.
System.out.println(d2);

打印

0.30000000000000004
0.3