考虑Java语言规范中的这个片段。
class Test {
public static void main(String[] args) {
int i = 1000000;
System.out.println(i * i);
long l = i;
System.out.println(l * l);
}
}
输出
-727379968
1000000000000
为什么-727379968
的结果为(i*i)
?理想情况下应该是1000000000000。
我知道Integer的范围是从-2147483648到2147483647.所以显然是1000000000000 不在给定范围内。
为什么结果会变成-727379968
?
答案 0 :(得分:8)
Java(与目前大多数计算机体系结构一样)使用名为two's complement arithmetic的东西,它使用整数的最高位来表示数字是负数。如果你乘以两个大数字,你最终会得到一个如此大的数字,它设置最高位,结果最终为负数。
答案 1 :(得分:6)
您可能希望将Integer overflow视为一般概念。 根据语言的不同,溢出和下溢的处理方式也不同。这是关于Integer overflow and underflow in Java的文章。
至于Java语言中的原因,与往常一样,它是语言设计的简单性和性能之间的权衡。但是在Java puzzlers(谜题3)中,作者批评了Java中溢出是静默的事实:
语言设计师的教训是,值得减少 无声溢出的可能性。这可以通过提供支持来完成 对于不会无声溢出的算术。程序可能会抛出 一个例外而不是溢出,就像Ada一样,或者他们可以切换 根据需要自动更大的内部表示以避免 溢出,Lisp也是如此。这两种方法都可能具有性能 与他们相关的处罚。另一种减少可能性的方法 静默溢出是为了支持目标类型,但这会增加 类型系统非常复杂。
答案 2 :(得分:3)
其他一些答案正确解释了为什么会发生这种情况(即签署了两个恭维二进制逻辑)。
问题的实际解决方案以及如何在使用非常大的数字时使用Java获得正确的答案是使用BigInteger类,它也适用于长值。
package com.craigsdickson.scratchpad;
import java.math.BigInteger;
public class BigIntegerExample {
public static void main(String[] args) {
int bigInt = Integer.MAX_VALUE;
// prints incorrect answer
System.out.println(bigInt * bigInt);
BigInteger bi = BigInteger.valueOf(bigInt);
// prints correct answer
System.out.println(bi.multiply(bi));
long bigLong = Long.MAX_VALUE;
// prints incorrect answer
System.out.println(bigLong * bigLong);
BigInteger bl = BigInteger.valueOf(bigLong);
// prints correct answer
System.out.println(bl.multiply(bl));
}
}
答案 3 :(得分:2)
让我们看看二进制文件:
1000000是1111 0100 0010 0100 0000
1000000000000是1110 1000 1101 0100 1010 0101 0001 0000 0000 0000
然而,4位的前两个部分不适合int
(因为int
在Java中是32位宽),因此它们是已删除,仅保留1101 0100 1010 0101 0001 0000 0000 0000
,即-727379968
。
换句话说,结果会溢出int
,你会得到剩下的东西。
答案 4 :(得分:0)
在其他答案中已经解释了出现整数溢出的原因。
确保计算中长算术的一种实用方法是使用带有long
后缀的数字文字,将文字声明为jshell> 100000 * 100000
$1 ==> -727379968
。
溢出的普通整数乘法:
l
乘法,其中一个被乘数具有jshell> 100000 * 100000l
$1 ==> 1000000000000
后缀,不会溢出:
long
请注意,-9,223,372,036,854,775,808
也容易出现溢出,但范围要大得多,从9,223,372,036,854,775,807
到scale_fill_gradientn
。