为什么以下两个操作在x = 31
或32
的Java中产生不同的结果,但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
答案 0 :(得分:18)
有多个问题在起作用:
int
只能存储-2147483648
和2147483647
之间的值。1 << x
仅限uses the lowest five bits of x
。因此,1 << 32
的定义与1 << 0
相同。1 << 31
是否定的。Math.pow(2, 32)
会返回double
。(int)(d)
,其中d
是double
大于2147483647
,返回2147483647
("the largest representable value of type int
")。此采访问题的作用表明(int)Math.pow(2, x)
和1 << x
与x
... 0
范围之外的30
值不相等。
P.S。或许有趣的是,使用long
代替int
(和1L
代替1
)会给出另一组与其他两个不同的结果。即使最终结果转换为int
,这也成立。
答案 1 :(得分:3)
根据文档Math.pow
将它的两个参数提升为double并返回double。显然当返回的结果是double并且你将它转换为int时,你将只得到最高的32位,其余的将被截断 - 因此你总是得到(int) Math.pow(2,x);
值。当你进行bitshift时,你总是使用整数,因此会发生溢出。
答案 2 :(得分:2)
考虑int类型的限制。它能保持多大的数量?
答案 3 :(得分:0)
int的大小为32位,由于它是有符号的(默认情况下),因此第一位用于符号。向左移31位时,得到Two's Compliment,即 - (2 ^ 32)。当你向左移动32位时,它只是一直循环回到1.如果你用long而不是int进行移位,你会得到你期望的答案(直到你移动63+位)。 / p>
答案 4 :(得分:0)
这是一个长期案例的微观基准。在我的笔记本电脑(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
}