This is the classic Problem from the very good book , Java Puzzlers Link to Book
Puzzle 3: Long Division
This puzzle is called Long Division because it concerns a program that divides
two long values. The dividend represents the number of microseconds in a day;
the divisor, the number of milliseconds in a day. What does the program print?
public class LongDivision {
public static void main(String[] args) {
final long MICROS_PER_DAY = 24 * 60 * 60 * 1000 * 1000;
final long MILLIS_PER_DAY = 24 * 60 * 60 * 1000;
System.out.println(MICROS_PER_DAY / MILLIS_PER_DAY);
}
}
Solution 3: Long Division
This puzzle seems reasonably straightforward. The number of milliseconds per
day and the number of microseconds per day are constants.
For clarity, they are expressed as products.
The number of microseconds per day is (24 hours/day · 60
minutes/hour · 60 seconds/minute · 1,000 milliseconds/second · 1,000 microseconds/millisecond).
The number of milliseconds per day differs only in that it is
missing the final factor of 1,000. When you divide the number of microseconds
per day by the number of milliseconds per day, all the factors in the divisor cancel
out, and you are left with 1,000, which is the number of microseconds per millisecond. Both the divisor and the dividend are of type long, which is easily large
enough to hold either product without overflow.
It seems, then, that the program
must print 1000. Unfortunately, it prints 5. What exactly is going on here?
The problem is that the computation of the constant MICROS_PER_DAY does
overflow. Although the result of the computation fits in a long with room to spare,
it doesn’t fit in an int. The computation is performed entirely in int arithmetic,
and only after the computation completes is the result promoted to a long. By
then, it’s too late: The computation has already overflowed, returning a value that
is too low by a factor of 200. The promotion from int to long is a widening primitive conversion [JLS 5.1.2], which preserves the (incorrect) numerical value. This
value is then divided by MILLIS_PER_DAY, which was computed correctly because
it does fit in an int. The result of this division is 5.
So why is the computation performed in int arithmetic? Because all the factors that are multiplied together are int values. When you multiply two int values, you get another int value. Java does not have target typing, a language
feature wherein the type of the variable in which a result is to be stored influences
the type of the computation.
It’s easy to fix the program by using a long literal in place of an int as the
first factor in each product. This forces all subsequent computations in the expression to be done with long arithmetic. Although it is necessary to do this only in
the expression for MICROS_PER_DAY, it is good form to do it in both products. Similarly, it isn’t always necessary to use a long as the first value in a product, but it is good form to do so. Beginning both computations with long values makes it clear that they won’t overflow.
This program prints 1000 as expected:
public class LongDivision {
public static void main(String[] args) {
final long MICROS_PER_DAY = 24L * 60 * 60 * 1000 * 1000;
final long MILLIS_PER_DAY = 24L * 60 * 60 * 1000;
System.out.println(MICROS_PER_DAY / MILLIS_PER_DAY);
}
}
The lesson is simple: When working with large numbers, watch out for
overflow—it’s a silent killer. Just because a variable is large enough to hold a
result doesn’t mean that the computation leading to the result is of the correct
type. When in doubt, perform the entire computation using long arithmetic.
The lesson for language designers is that it may be worth reducing the likelihood of silent overflow. This could be done by providing support for arithmetic
that does not overflow silently. Programs could throw an exception instead of
overflowing, as does Ada, or they could switch to a larger internal representation
automatically as required to avoid overflow, as does Lisp. Both of these
approaches may have performance penalties associated with them. Another way
to reduce the likelihood of silent overflow is to support target typing, but this adds
significant complexity to the type system [Modula-3 1.4.8].