今天运行一个程序,发现一个很有意思的情况,可以看到代码如下所示
public static void main(String[] args) {
float a = 1;
for (int i = 0; i<20000000; i++) {
a++;
}
System.out.println(a);
}
public static void main(String[] args) {
float a = 20000000;
float b = a + 1;
float c = b - a;
System.out.println(b);
System.out.println(c);
}
上面两段代码的输出结果是什么呢?输出结果如下:
1.6777216E7
2.0E7
0.0
好像和我们预期的不太一样,为什么会出现这种情况呢?那就要从我们的计算机底层说起,计算机内只有0,1来表示内容,对于整数我们知道可以转化为2进制去表示,那么对于小数,计算机内部是怎么表示的呢?在计算机内部有一个公式来表示小数;公式如下:
以32位计算机来说
其中:s-为符号位。占用一个bit位,表示是正数还是负数的作用。
e-为指数位,占用8个bit位,也就是可以表示0~255个数字。
f-为有效位,占用23个bit位,用来表示小数点后的有效位数。
以小数0.5为例,转化成上面的公式就表示如下:
为什么浮点数进行相加会出现精度丢失呢?
首先我们要知道浮点数进行相加需要遵守的原则就是先对齐,在计算,因为两个浮点数进行加法运算的时候,可能指数位是不一致的,所以需要先把指数位保持一致,然后再去计算有效位的加法就可以了。
以0.5 + 0.125为例:
数字 | s位大小 | e位大小 | f位 |
0.5 | 0 | -1 | 1.0... |
0.125 | 0 | -3 | 1.00... |
0.125向0.5进行指数对齐后 | 0 | -1 | 0.01.... |
所以计算结果为
所以在加法运算过程中,指数位较小的数,需要在有效位进行右移,在右移的过程中,最右侧的有效位就被丢弃掉了。这会导致对应的指数位较小的数,在加法发生之前,就丢失精度。其中有效位置有23位,当两个数的指数位相差23位后,对应的右移23位后,所有的有效位全部丢失,所以就会出现相面的计算情况。