前言
昨晚的一堂课确实有些颠覆了认知,因为感觉到虽然自己从事java多年,却未曾理解计算机的世界。你是不是跟我一样,对计算机的世界的理解就是:不就是0和1吗?其实这太表面了。。
经典题目
题目一
float a = 0.5F - 0.25F;
float b = 0.25F- 0.0F;
Float c = Float.valueOf(a);
Float d = Float.valueOf(b);
System.out.println(a == b);
System.out.println(c.equals(d));
题目二
float a = 1.0F - 0.9F;
float b = 0.9F- 0.8F;
Float c = Float.valueOf(a);
Float d = Float.valueOf(b);
System.out.println(a == b);
System.out.println(c.equals(d));
答案
题目一是两个true,而题目二则是两个false。是不是有点懵?欢迎来到计算机的世界!
计算机是如何表示小数的
- 计算机只有0和1。这意味着所有我们习以为常的十进制的数,都要先转换为二进制,才能被计算机所理解。
- 小数在计算机中是以《科学记数法》来表示的。原因嘛,第一,小数位可能是无限的。例如圆周率。如果存储空间不限,理论上可以精确表示。然鹅,我们计算机的存储是有限的。其次,科学记数法本身就是用来表示极大数和极小数的,正好可以利用这一点。但是,我们都知道使用科学记数法都会涉及到有效位数的问题。如果一个数在有效位数内刚好能够完全表示,那么这个数就是准确无误的,而如果不能够完全表示,则面临着四舍五入的命运,即精度丢失!
- 上面这两条大家应该都认识,但是可能组合在一起,就理解不到位了。因为十进制中的小数,要先转换成二进制中的小数,然后再使用科学计数法才能够表示。而科学技术法的有效位数取决于我们使用的是Float还是Double.
计算机世界诡异的小数
由于存在进制转换,这使得有限的小数也可能变成无线小数。
- 在十进制中的1/3,在三进制中就是0.1
- 在十进制中的0.9,在二进制中竟然是个无线循环小数。
- 在十进制中的0.5,在二进制中变成了0.1
在我理解看来,从物理学的角度去看,就是参考系发生了变化。这个神奇的地方,让我不禁猜想会不会我们所熟知的圆周率,在某个进制转换下,就是一个有限的正常的小数???也就瞎想一下哈,别当真。
回到题目
现在再回来看,能理解了吗?题目一中的小数,都可以被二进制完整的表示,而题目二中的小数却不能。小数0.9迫于存储空间的原因,被使用科学记数法截取了,因此精度丢失了。
- 十进制到二进制的小数
- 0.5(1/2) -> 0.1
- 0.25(1/4) -> 0.01
- 0.125(1/8) -> 0.001
总结
- 小数从十进制到二进制转换后,可能是个无限小数。
- 小数在计算机世界是用科学记数法表示的,可能存在精度丢失。
- 小数从十进制转换成二进制进入计算机的世界后,可能存在精度丢失。只有当分母可以被二进制整除才不会丢失精度。