我知道以下行为是一个老问题,但我仍然不明白。
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
?
我该如何解决这个问题?
答案 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()更正,所以你看到舍入错误。
故事的寓意,如果你使用float
或double
也适当地围绕解决方案。
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