Java Reference
In-Depth Information
Exception in thread "main" ArrayIndexOutOfBoundsException: -2
at Mod.main(Mod.java:9)
What is going on here?
The problem lies in the program's use of the
Math.abs
method, which results in erroneous mod 3
values. Consider what happens when
i
is -2. The program computes the value
Math.abs(-2) % 3
,
which is
2
, but the mod 3 value of -2 is 1. This would explain incorrect numerical results, but it
leaves us in the dark as to why the program throws an
ArrayIndexOutOfBoundsException
. This
exception indicates that the program is using a negative array index, but surely that is impossible:
The array index is calculated by taking the absolute value of
i
and computing the remainder when
this value is divided by 2. Computing the remainder when a nonnegative
int
is divided by a
positive
int
is guaranteed to produce a nonnegative result [JLS 15.17.3]. Again we ask, What is
going on here?
To answer that question, we must go to the documentation for
Math.abs
. This method is named a
bit deceptively. It nearly always returns the absolute value of its argument, but in one case, it does
not. The documentation says, "If the argument is equal to the value of
Integer.MIN_VALUE
, the
result is that same value, which is negative." Armed with this knowledge, it is obvious why the
program throws an immediate
ArrayIndexOutOfBoundsException
. The initial value of the loop
index
i
is
Integer.MIN_VALUE
, which generates an array index of
Math.abs(Integer.MIN_VALUE)
% 3
, which equals
Integer.MIN_VALUE % 3
, or
2
.
To fix the program, we must replace the bogus mod calculation (
Math.abs(i) % MODULUS
) with
one that actually works. If we replace this expression with an invocation of the following method,
the program produces the expected output of
1431655765 1431655766 1431655765
:
private static int mod(int i, int modulus) {
int result = i % modulus;
return result < 0 ? result + modulus : result;
}
The lesson of this puzzle is that
Math.abs
is not guaranteed to return a nonnegative result.
If its
argument is
Integer.MIN_VALUE
— or
Long.MIN_VALUE
for the
long
version of the method— it
returns its argument. The method is not doing this just to be ornery; this behavior stems from the
asymmetry of two's-complement arithmetic, which is discussed in more detail in
Puzzle 33
. Briefly,
there is no
int
value that represents the negation of
Integer.MIN_VALUE
and no
long
value that
represents the negation of
Long.MIN_VALUE
. For library designers, it might have been preferable if
Math.abs
threw
IllegalArgumentException
when it was passed
Integer.MIN_VALUE
or
Search WWH ::
Custom Search