2147483647 是能够容纳在 Java 中 int
类型中的最大数值。实际上,int
并不意味着“任何整数”,而是指:一个能适应32位空间的有符号(采用二补数表示)整数。
关键在于,231-1 是最大的正整数“边界”,而 -231 是最大的负整数“边界”。这些数值会发生溢出:
int x = 2147483647;
System.out.println(x == Integer.MAX_VALUE); // 输出为 true,表明 x 等于 int 的最大值
x++;
System.out.println(x); // 输出为 -2147483648,表示发生了溢出
System.out.println(x == Integer.MIN_VALUE); // 输出为 true,表明溢出后 x 成为了 int 的最小值
x--;
System.out.println(x); // 输出为 2147483647,恢复到原来的最大值
至关重要的是,当 n
是 int 能表示的最大值时,条件 p * p <= n
永远不可能为假,因为没有比它更大的数了。因此,在这种情况下,您的 while
循环会无休止地执行下去。最终经过大量循环之后,变量 p
达到 2147483647,然后进入下一次循环,此时 p
变为 -2147483648。继续循环很多次后(事实上,需要循环4294967293次),p
最终变为1。这时终于满足 n % p
等于0,于是 isPrime
被设置为 false,这是在这种情况下唯一能让循环结束的方法——通过令 isPrime
为 false,考虑到 p * p <= n
在此条件下无法为假(对于任何数 X,若 n
为 int 类型的最大值,则 X <= n
总是成立的,因为没有任何数能大于或等于已知的最大整数值)。
解决方案如下:
使用 long
类型代替。这会提供更多的存储空间;long 类型同样是整数类型,但占用64位,因此可以表示从 263-1 到 -263 的数值范围,即大约18位数字大小。不过,如果输入恰好是 263-1,仍然会有问题,除非……你继续使用 .nextInt()
方法读取整数,但是将其 强制转换 为 long 类型存储。这样就避免了上述问题。
特殊处理这种情况。真正的问题在于 p*p
变为负数(因为结果超过 231-1 时会发生溢出),所以找到那个使该条件成立的临界值(即大于 sqrt(2147483647) 的所有数),你可以在此临界值之上停止循环,或者当 p*p
大于 n 时也停止。
使用 BigInteger
类型。现在你的代码可以处理任意大小的输入,尽管与高效的质数查找算法相比,其运行速度将会极其缓慢。