为什么总是不能正确求和两个浮点值?

问题描述:

如果你问我,下面的例子给出的结果是超级怪异的。这怎么可能?有没有办法将这两个值恰当地相加? Float.sum(float a,float b)也给了我相同的结果。为什么总是不能正确求和两个浮点值?

import java.text.DecimalFormat; 

public class HelloWorld { 
    public static void main(String[] args) { 
    System.out.println("Sum: " + new DecimalFormat("#").format((1500036225984102400.f + 2000000000.f))); 
    } 
} 

输出是:

总:1500036225984102400

我当然会期待这两个值得到总结,但它似乎是第二个值才刚刚被忽略?

如果你看看如何设置float(IEEE-754),你很快就会发现尾数只有23位(实际上是24位,其中一个对于非非规范化数是隐含的)。

1500036225984102400是十六进制的

14 d1 3300 00 00 00 00
所以你看,数字的非零部分适合这24位。这就是为什么你打印的数字完全按照你的要求打印的原因。任何进一步的比特将被简单地切断(类似于整数除法,尽管不完全相同)。其实,这在数学上是四舍五入的。

所以,如果你现在比较你的两个数字:

 
14 d1 3300 00 00 00 00 
      77 35 9400
你很快就会看到添加的结果不适合可用的24位,什么不适合,只是简单地舍掉了–正是你试图添加的数字...

这通常是一个浮点数的问题,但得到更少的可见与双倍尾数和指数都只是更大的那里...

浮点数的精度有限。您可以使用Math.nextUp方法找到可表示的下一个最大号码。例如,你可以使用找到一个比你的第一个操作数的下一个最大数:

float next = Math.nextUp(1500036225984102400.f); 

可以使用Math.ulp得到一个编号,它的下一个最大的数或者通过减之间,或跳转的大小,它告诉你一个“单位在最后的地方”的大小,如果你调整最低显著位数量的增加量:

float ulp = Math.ulp(1500036225984102400.f); 

你会发现,这个数字大于2000000000.f

Math.ulp(1500036225984102400.f) = 137438953472.0 
            2000000000.0 

因此,您的两个数字的总和不能用float1500036225984102400.f更精确地表示。