为什么总是不能正确求和两个浮点值?
问题描述:
如果你问我,下面的例子给出的结果是超级怪异的。这怎么可能?有没有办法将这两个值恰当地相加? 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
因此,您的两个数字的总和不能用float
比1500036225984102400.f
更精确地表示。