为什么以下两个操作在 Java 中会产生不同的结果,x = 31
或者32
会产生相同的结果x=3
?
int x=3;
int b = (int) Math.pow(2,x);
int c = 1<<x;
结果:
x=32: b=2147483647; c=1;
x=31: b=2147483647; c=-2147483648;
x=3: b=8 ; c=8
为什么以下两个操作在 Java 中会产生不同的结果,x = 31
或者32
会产生相同的结果x=3
?
int x=3;
int b = (int) Math.pow(2,x);
int c = 1<<x;
结果:
为什么以下两个操作在 Java 中会产生不同的结果,x = 31
或者32
会产生相同的结果x=3
?
int x=3;
int b = (int) Math.pow(2,x);
int c = 1<<x;
结果:
x=32: b=2147483647; c=1;
x=31: b=2147483647; c=-2147483648;
x=3: b=8 ; c=8
考虑 int 类型的限制。它可以容纳多大的数字?
有多个问题在起作用:
int
只能存储 和 之间-2147483648
的值2147483647
。1 << x
仅使用 的最低五位x
。因此,1 << 32
根据定义,与 相同1 << 0
。1 << 31
是负面的。Math.pow(2, 32)
返回一个double
。(int)(d)
,其中d
adouble
大于2147483647
返回2147483647
(“类型的最大可表示值int
”)。这个面试问题的作用是表明(int)Math.pow(2, x)
并且对于超出...范围1 << x
的值是不等价的。x
0
30
PS 有趣的是,使用long
in place of int
(和1L
in place of 1
)会给出另一组与其他两个不同的结果。即使最终结果转换为int
.
根据文档,Math.pow
将把它的两个参数都提升为双倍并返回双倍。显然,当返回的结果为 double 并且您将其转换为 int 时,您将只获得最高的 32 位,其余的将被截断 - 因此您始终会获得该(int) Math.pow(2,x);
值。当您进行位移时,您总是使用整数,因此会发生溢出。
考虑 int 类型的限制。它可以容纳多大的数字?
这是一个长期案例的微基准。在我的笔记本电脑 (2.8GHz) 上,使用 shift 而不是Math.pow
快 7 倍以上。
int limit = 50_000_000;
@Test
public void testPower() {
Random r = new Random(7);
long t = System.currentTimeMillis();
for (int i = 0; i < limit; i++) {
int p = r.nextInt(63);
long l = (long)Math.pow(2,p);
}
long t1 = System.currentTimeMillis();
System.out.println((t1-t)/1000.0); // 3.758 s
}
@Test
public void testShift() {
Random r = new Random(7);
long t = System.currentTimeMillis();
for (int i = 0; i < limit; i++) {
int p = r.nextInt(63);
long l = 1L << p;
}
long t1 = System.currentTimeMillis();
System.out.println((t1-t)/1000.0); // 0.523 s
}
int 大小为 32 位,并且由于它是有符号的(默认情况下),因此第一位用于符号。当您向左移动 31 位时,您会得到Two's Compliment,即 -(2^32)。当您向左移动 32 位时,它只会一直循环回到 1。如果您要使用 long 而不是 int 进行这种移动,您会得到您期望的答案(即直到您移动 63+ 位)。