这是在其他地方看到的一个提问,提问原文链接:https://www.oschina.net/question/2346828_2266386?sort=default#comments
如下代码示例,问0.7在计算机中应该是无法精确存储的,那为什么可以输出0.7?
public void test() {
double a = 0.7;
System.out.println(a);
}
看了几个答案,感觉都没有明确回答问题,或是没有说清楚,我试着解答一下。看提问的代码是java,所以我也用java回答,先看如下的代码:
public static void main(String[] args) {
float a = 0.7f;
float b = 0.69999996f;
float c = 0.70000001f;
System.out.println(a); //0.7
System.out.println(b); //0.7
System.out.println(c); //0.7
System.out.println(Integer.toUnsignedString(Float.floatToIntBits(a), 16));//3f333333
System.out.println(Integer.toUnsignedString(Float.floatToIntBits(b), 16));//3f333333
System.out.println(Integer.toUnsignedString(Float.floatToIntBits(c), 16));//3f333333
System.out.println(a==b); //true
System.out.println(a==c); //true
float d = 0.70000002f;
System.out.println(d); //0.70000005
System.out.println(Integer.toUnsignedString(Float.floatToIntBits(d), 16));//3f333334
}
第一个问题: 提问者说0.7在计算机中应该是无法存储的。
不是应该,而是肯定无法精确存储,因为java中也遵循IEEE 754浮点数的二进制表示规范,所以肯定无法精确存储。上面代码中a=0.7f, b=0.69999998f, c=0.70000001的值在java中都是3f333333,在java看来它们是一样的,都是0.7。按IEEE 754标准手工计算一下,3f333333换算回来大约是0.699999988,当然这个值的最后一位在java中是省略的。
第二个问题:为什么可以输出0.7?
明白了0.7在java内存中的存储形式,就知道了,在java中0.7的表示形式就是3f333333,这个就是0.7。不管你开始给变量赋值是用a, b 还是c, 他们在内存中的表示是一样的,所以输出都是0.7。所以根本就不存在为什么可以输出0.7的问题,因为是0.7肯定要输出0.7。
其实提问者想搞明白的问题是:为什么不是输出0.699999988这样的形式。
这个其他回答其实也解释了,就是精度问题,但没有说透,我再来解释一下。
以IEEE 754单精度为例,在位数固定32的情况下,尾数部分的位数23位也是固定的,所以需要用超过23位才能表示的数,超过部分就被省略了,示例的0.7就是这样的数,有省略。这个角度是一般人都理解的。
但还有另一个角度,就是数的个数问题。大部分人对IEEE 754单精度能表示的数字范围(±3.4×10^38)没有深入的理解。固定32位的长度能够表示的数字个数是有限的,表示范围确定以后,在一个小的区间中,能够表示的数字的个数就很有限了。两个值比较接近的浮点数相减,则会产生一个极小的浮点数,这种情况下如果省略过多,则大量计算就无法精确表示了,基本都是0了。这也是为什么IEEE754规定了两种形式的浮点数:规约形式与非规约形式。其目的就是:让有限的能表示的数的个数,在整个表数范围内,不再均匀分配。
有了这两个角度,那就可以想明白了。若把IEEE 754能表示的所有数都画在一条数轴上,那么会看到能表示的两个数字之间是有间隔的,且间隔在0的附近比其他地方要小,也就是在0附近数比较密集。在能表示的两个数字之间的数,在计算机看来都是同一个数。如:a=0.7f, b=0.69999998f, c=0.70000001 是同一个数,这里要注意不是说的b最后一位进位了,c的最后一位省略了。而是这几个数在按IEEE 754标准存到计算机中时,尾数23位满了,但后边还有,把后边的省略了。省略之后,剩下的全部一样,所以计算机认为他们是一个数。
如果看到这里还表示不明白,那就只能用代码中的d = 0.70000002f 这个例子说明一切了。因为这个变量d输出值是 0.70000005,明显这输入计算机的值和输出的值根本不一样。
最后,千言万语化成一句解释:输入0.7,输出也是0.7,只是碰巧而已,换个数就没这么幸运了。