Like the program in puzzle 26 and 27, the program below has a single-weight loop that records the number of iterations and prints this number at the end of the loop. So what will this program print?
public class Count { public static void main(String[] args) { final int START = 2000000000; int count = 0; for (float f = START; f < START + 50; f++) count++; System.out.println(count); }}
The surface analysis may assume that this program will print 50. After all, the loop variable (f) is initialized to 2,000,000,000, and the end value is 50 larger than the initial value, and this loop has the traditional "half open" form: it uses the <operator, which includes the initial value but does not include the termination value.
However, this analysis misses the key point: the cyclic variable is of the float type, rather than the int type. Think back to puzzle 28. Obviously, incremental operations (F ++) cannot work normally. The initial value of F is close to integer. max_value. Therefore, it must be expressed accurately with 31 bits, while the float type can only provide 24 bits. Incremental operations on such a huge float value will not change its value. Therefore, this program seems to be infinite, because F will never solve its termination value. However, if you run the program, you will find that it does not have an infinite loop. In fact, it immediately terminates and prints 0. What's going on?
The problem is that the conditional test fails, and the method is similar to that of the incremental operation failure. This loop runs only when the round-robin index F is smaller than (float) (start + 50. When an int is compared with a float, the upgrade from int to float is automatically executed [JLS 15.20.1]. Unfortunately, this improvement will lead to loss of precision among the three types of original type conversion [JLS 5.1.2]. (The other two are from long to float and from long to double .)
The initial value of F is too large, so that when 50 is added to it and the result is converted to float, the resulting value is equivalent to directly converting F to the float value. In other words, (float) 2000000000 = 2000000050, so the expression F <start + 50 is false even before the first execution of the loop body, so the loop body will never run.
It is very easy to correct this program. You only need to change the type of the loop variable from float to int. This avoids all inaccuracy related to floating point calculation:
for (int f = START; f < START + 50; f++) count++;
If you do not use a computer, how do you know that 2,000,000,050 and 2,000,000,000 have the same float representation? The key is to observe that 2,000,000,000 has 10 Factors of 2: it is a 2 multiplied by 9 10, and each 10 is 5 × 2. This means that the binary representation of 2,000,000,000 ends with 10 zeros. The binary value of 50 indicates that only six digits are required. Therefore, adding 50 to 2,000,000,000 does not affect other values except the 6 digits on the right. In particular, the number of 7th and 8th digits from the right side is still 0. To increase the 31-bit int to a float with 24-bit precision, the 31-bit float will be rounded between the 7th-bit and the 8th-bit, thus directly dropping the rightmost 7-bit. The rightmost 6 bits are 2,000,000,000 and 2,000,000,050 bits, so their float representation is the same.
The implication of this puzzle is simple: Do not use floating-point numbers as cyclic indexes because they can lead to unpredictable behavior. If you need a floating point number in a loop, use an int or long loop index and convert it to float or double. When converting an int or long into a float or double, you may lose precision, but at least it will not affect the Loop itself. When you use floating point numbers, you must use double instead of float, unless you are sure that float provides sufficient precision and there are mandatory performance requirements that force you to use float. It is very rare to use float instead of double.
It is very confusing for programmers to quietly lose precision for the lessons learned by Language designers. For more information about this, see question 31.
Puzzle 34: knocked down by counting