奇怪的Java分数行为

时间:2012-04-25 10:08:36

标签: java fractions

我在Java的双变量中看到了一个非常奇怪的行为,因为我试图简单地将小分数添加到双精度中,我看到一个完全奇怪的结果。

double test = 0;
test += 0.71;
test += 0.2;

现在我希望结果是:

test = 0.91

右?错!

实际上,这是我在测试中获得的数字:

test = 0.9099999999999999

现在虽然非常接近,但这是一个非常奇怪的分数损失,从长远来看,它会导致我的程序出现严重错误。

随着浮动,我甚至得到了更奇怪的结果。

非常感谢任何帮助。

由于

4 个答案:

答案 0 :(得分:4)

根本没什么奇怪的。 0.91,0.71和0.2不能表示为IEEE754浮点值,因为它们在以二进制表示时将具有重复的小数部分。这种情况完全类似于试图用有限数字的数字表示基数10的1/3。你不能这样做。

您所看到的是进行浮点计算时正常的舍入误差。你必须围绕它编码。因此,例如,您无法可靠地比较相等性,您必须看到这两个数字在彼此的某个小的delta内。有关更深入但仍可理解的解释,请参阅The Floating Point Guide

答案 1 :(得分:1)

这是浮点值二进制编码的神奇之处(寻找IEEE754:http://en.wikipedia.org/wiki/IEEE_754-2008)。如果你想确保永远不会有这种东西,你可能正在寻找BigDecimal:

http://docs.oracle.com/javase/1.5.0/docs/api/java/math/BigDecimal.html

基本规则:

  • 在处理浮点数时不要使用相等测试(必须测试间隙)
  • 您正在显示的数字(通常使用DecimalFormat)
  • 不要为财务应用程序使用浮点数
  • 只要您了解IEEE754
  • ,浮标通常是科学或工业操作的方式

答案 2 :(得分:1)

double只能估算大多数小数值。这意味着如果您想获得预期结果,则需要使用一些舍入。或者您可以使用BigDecimal来处理这个问题。

double test = 0;
test += 0.71;
test += 0.2;
System.out.printf("%.2f%n", test);

打印

0.91

为了您自己的利益

System.out.println("0.71 is actually " + new BigDecimal(0.71));
System.out.println("0.2 is actually " + new BigDecimal(0.2));
System.out.println("0.71+0.2 is actually " + new BigDecimal(0.71 + 0.2));
System.out.println("0.91 is actually " + new BigDecimal(0.91));
System.out.println("0.71+0.2 == 0.91 is " + (0.71 + 0.2 == 0.91));

打印

0.71 is actually 0.70999999999999996447286321199499070644378662109375
0.2 is actually 0.200000000000000011102230246251565404236316680908203125
0.71+0.2 is actually 0.9099999999999999200639422269887290894985198974609375
0.91 is actually 0.91000000000000003108624468950438313186168670654296875
0.71+0.2 == 0.91 is false

答案 3 :(得分:0)

Java使用称为浮点的东西来表示小数。它们使用指数表示法。这就是我的意思:

有乘数(M),指数介于1023和-1022(E)之间。

数字(N)表示如下:M * 2^E

4.25表示如下:

17 * 2 ^ -2。

0.91不能完全用基数2表示,但Java可以非常接近:

0.909999999999 ..

因此,无法准确地将这些数字加在一起。