对float或double值执行重复添加会产生意外结果

时间:2013-12-05 17:02:26

标签: java floating-point floating-accuracy

我发现了一个但是在我的代码中,经过一些搜索,我意识到我正在执行的一些浮动操作产生了错误的结果。所以我输入了以下循环:

 float f = 0f;
     for(int i =0; i<15; i++){
        f+= 10.1f;
        System.out.println(f);
}

但是在结果中我得到了意想不到的额外小数值:

10.1
20.2
30.300001
40.4
50.5
60.6
70.7
80.799995
90.899994
100.99999
111.09999
121.19999
131.29999
141.4
151.5

这里发生了什么,我该如何预防?

3 个答案:

答案 0 :(得分:3)

你不能“阻止”它。你需要做的是期待和补偿。

请参阅:Every Computer Scientist Should Know About Floating-Point Arithmetic

答案 1 :(得分:3)

数字存储为二进制,并且在一定精度下,二进制和十进制之间的表示不完美,实质上导致“舍入错误”。如果精度在这种情况下很重要,请尝试使用BigDecimal

答案 2 :(得分:1)

代码中的两个操作会导致错误:

  • 将源文本10.1f转换为float。这种转换是不精确的,因为10.1无法用Java使用的浮点格式精确表示。
  • 10.1f添加到先前的f值。在许多这些添加中,精确的总和不能完全适合浮点格式,因此结果必须是四舍五入的。

您可以修改循环以避免累积错误:

for (int i = 0; i < 15; i++)
{
    f = (i+1) * 101 / 10.f;
    System.out.println(f);
}

当您以这种方式编写代码时,f在每次迭代中仍然不会完全是10.1•( i +1),但它将是最接近的可表示值。在这个新版本中,(i+1) * 101的确切计算,10.f正好是10,因为10可以浮点格式表示。这意味着唯一的错误是在除法运算中。该操作将返回最接近确切结果的可表示值。

Java使用IEEE-754二进制浮点,float为32位,double为64位。在这些格式中,数字基本上表示为2的幂的整数倍。在32位格式中,整数必须具有小于2 24 的幅度。在32位格式中,最接近10.1的是5295309•2 -19 ,即10.1000003814697265625。