如下代码:
public class Example034 { public static void main(String[] args) { int count = 0; int start = 2000000000; for (float f = start; f < start + 64; f++) { count++; } System.out.println(count); count = 0; System.out.println("将循环控制条件的值加1后……"); for (float f = start; f < start + 65; f++) { count++; } System.out.println(count); } }
输出结果:
0 将循环控制条件的值加1后……
结果分析:
分析第一次循环,该循环只有在循环控制变量f比(float)(start+64)小的时候才执行。(start+64)初始类型是int,被自动执行从int到float的提升,这种提升是以损失精度为代价的。由于f的初值太大,以至于在对其加上 64,然后将结果转型为 float 时,所产生的数值等于直接将 f 转换成 float 的数值。所以第一次循环并未执行,打印了结果0。但是,第一次循环执行完成打印后,为什么第二次的循环却成为了无限循环,没有任何输出结果呢?
首先我们先了解一下,如何知道两个很大的float是否相等呢?比如2000000000和2000000064。关键是要观察到 2, 000, 000, 000 有 10 个因子都是 2:它是一个 2 乘以 9 个 10,而每个 10 都是 5× 2。(2*(5*2)^9 == 2^10*5^9,2的10次方乘以5的9次方),这意味着 2, 000, 000, 000 的二进制表示是以10 个 0 结尾的。 64 的二进制表示只需要 7 位,所以将 64 加到 2, 000, 000, 000上不会对右边7 位之外的其他位产生影响。 特别是, 从右边数过来的第 8 位仍旧是 0。 提升这个 31 位的 int 到具有 24 位精度的 float 会在第 8 位四舍五入, 从而直接丢弃最右边的 7 位。而最右边的 7 位是2, 000, 000, 000 与 2, 000, 000, 064 的不同之处,因此它们的 float 表示是相同的。(蓝字部分内容表示不确定正确性)
如果按照上述讲解,那么上述代码的第二个循环应该同样不可以执行的。但是,事实上,第二个循环执行了,但是是死循环。这是为什么呢?
总之,不要使用浮点数作为循环索引 ,因为它会导致无法预测的行为。如果你在循环体内 需要一个浮点数,那么请使用 int 或 long 循环索引 ,并将其转换为 float 或 double。在将一个 int 或 long 转换成一个 float 或 double时,你可能会丢失精度,但是至少它不会影响到循环本身。当你使用浮点数时,要使用 double 而不是 float,除非你肯定 float 提供了足够的精度,并且存在强制性的性能需求迫使你使用 float。