The following program is imitating a simple clock. Its cyclic variable represents a millisecond counter, and its Count value starts from 0 until the number of milliseconds contained in an hour. The loop body performs incremental operations on one minute counter at regular intervals. Finally, the program prints the minute counter. So what does it print?
public class Clock { public static void main(String[] args) { int minutes = 0; for (int ms = 0; ms < 60*60*1000; ms++) if (ms % 60*1000 == 0) minutes++; System.out.println(minutes); }}
The loop in this program is a standard regular for loop. It step into the millisecond counter (MS), from 0 to the number of milliseconds in an hour, that is, 3,600,000, including the former but not the latter. The loop body appears to perform incremental operations on the minute counter (minutes) whenever the counter Count value in milliseconds is a multiple of 60,000 (the number of milliseconds included in one minute. This occurs 3,600,000/60,000 times in the cycle, that is, 60 times, so you may expect the program to print 60. After all, this is the number of minutes in an hour. However, the running of the program tells you another scene: It prints 60000. Why does it perform incremental operations on minutes so frequently?
The problem is that the Boolean expression (MS % 601000 = 0 ). You may think this expression is equivalent to (MS % 60000 = 0), but they are not equivalent. The remainder and multiplication operators have the same priority [JLS 15.17], so the expression Ms % 601000 is equivalent to (MS % 60) * 1000. If (MS % 60) is equal to 0, this expression is equal to 0, so the loop performs incremental operations on minutes every 60 iterations. This makes the final result 1000 times different.
The simplest way to correct this program is to insert a pair of parentheses in a Boolean expression to force the correct order of calculation:
if (ms % (60 * 1000) == 0) minutes++;
However, there is a better way to revise the procedure. Replace all magic numbers with properly-named constants:
public class Clock { private static final int MS_PER_HOUR = 60 * 60 * 1000; private static final int MS_PER_MINUTE = 60 * 1000; public static void main(String[] args) { int minutes = 0; for (int ms = 0; ms < MS_PER_HOUR; ms++) if (ms % MS_PER_MINUTE == 0) minutes++; System.out.println(minutes); }}
The reason to present the expression MS % 60*1000 in the initial program is to induce you to think that multiplication has a higher priority than the remainder. However, the compiler ignores spaces. Therefore, do not use spaces to represent groups. use parentheses. Spaces are unreliable, and parentheses never lie.
Puzzle 35: minute after minute