Java BigDecimal精度问题

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

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

我怎么解决这个问题?

你真正想要的是什么

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

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

首先,永远不要使用BigDecimal的双构造函数。 在少数情况下它可能是正确的,但大多数情况下并非如此

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

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

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

尝试这个:

 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总是以浮点表示forms表示:

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

你遇到的问题是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