我想不需要太多解释,为什么下面的计算给出的结果是1?
int a = 2147483647;
int b = 2147483647;
int c = a * b;
long d = a * b;
double e = a * b;
System.out.println(c); //1
System.out.println(d); //1
System.out.println(e); //1.0
整数2147483647的二进制表示如下:
01111111 11111111 11111111 11111111
将其与自身相乘得到数字 4611686014132420609,其二进制表示为:
00111111 11111111 11111111 11111111 00000000 00000000 00000000 00000001
这对于只有 32 位的
int
类型来说太大了。 a * b
的乘法仅作为整数乘法完成,无论结果分配到的变量的类型如何(这可能会进行扩大转换,但仅在乘法之后)。
所以,结果只是截掉所有不适合32位的位,只留下以下结果:
00000000 00000000 00000000 00000001
这就是值 1。
如果要保留信息,必须与 64 位的
long
类型进行乘法:
long a = 2147483647;
long b = 2147483647;
long mult = a * b;
System.out.println((int) mult); // 1
System.out.println(mult); // 4611686014132420609
System.out.println((double) mult); // 4.6116860141324206E18
BigInteger
(对于整数)或 BigDecimal
(对于小数)。
2147483647 * 2147483647 = 4611686014132420609
十六进制 = 3FFFFFFF 00000001,截断后只剩下 00000001 代表 1。
首先,三次尝试都给出相同答案的原因是,它们都在执行 32 位乘法,并且乘法溢出,导致“信息丢失”。 信息溢出/丢失发生在将 RHS1 表达式的值分配给 LHS 上的变量之前。
在第二种和第三种情况下,您可以使用 64 位或浮点来计算表达式:
int c = a * b;
long d = ((long) a) * b;
double e = ((double) a) * b;
并且你不会溢出。至于为什么在 32 位情况下会出现溢出,很简单。 结果大于 32 位。 其他答案解释了为什么答案是
1
。只是为了好玩,这是一个非正式的证明。
假设我们正在讨论一个模数系统,其数字范围为 2
N-1 到 2N-1 - 1。在这样的数字系统中,X * 2N 映射到零......对于所有整数 X.
如果我们将最大值与其本身相乘,我们会得到
(2现在将其映射到原始范围:N-1 - 1) * (2N-1 - 1)
-> 22N-2 - 2 * 2N-1 + 1
-> 22N-2 - 2N + 1
22N-2 映射到 0
2N 地图 0
1 映射到 10 + 0 + 0 -> 1
1 - LHS == 左侧,RHS == 右侧。
Integer.MAX_VALUE + 1 = Integer.MIN_VALUE
了解正在发生的情况的一种方法是将 Java 的
int
类型设计为从
-7
到
7
,仍然适用相同的规则。 让我们看看当我们相乘时会发生什么
7*7
:
7 + 7 = 14 -> -2 (7 x 2)
-2 + 7 = 5 (7 x 3)
5 + 7 = 12 -> -4 (7 x 4)
-4 + 7 = 3 (7 x 5)
3 + 7 = 10 -> -6 (7 x 6)
-6 + 7 = 1 (7 x 7, one is leftover)
您的代码中也发生了同样的事情,
2147483647
根据以下情况溢出:
2147483647 + 1 = -2147483648
因为:
2147483647 * 2147483647 = 4611686014132420609
整数容量=42949672954611686014132420609% 4294967295 = 1