为什么下面两个步骤:在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;
结果:
x=32: b=2147483647; c=1;
x=31: b=2147483647; c=-2147483648;
x=3: b=8 ; c=8
有在作怪多个问题:
int
只能存储之间的值-2147483648
和2147483647
。 1 << x
只使用的最低五个比特x
。 因此, 1 << 32
是由定义相同1 << 0
。 1 << 31
为负。 Math.pow(2, 32)
返回一个double
。 (int)(d)
其中d
是一个double
大于2147483647
返回2147483647
( “类型的可表示的最大值int
” )。 这是什么面试问题确实是表明(int)Math.pow(2, x)
和1 << x
不等价的值x
外部0
... 30
范围内。
PS这也许是有趣的是,使用long
代替int
(和1L
代替1
)将给予另一套结果与其它两个不同的。 这适用即使最终结果转换为int
。
根据该文件Math.pow
同时促进其参数将翻一番,双倍返还。 显然,当返回的结果是双,你将它转换成int,你只会得到最高的32位,其余的将被截断-因此你总是得到(int) Math.pow(2,x);
值。 当你位位移你总是与整数工作,并因此发生溢出。
考虑int类型的限制。 它是如何大了一些可以持有?
int是在大小为32个比特,并且因为它是签名(默认)中,第一比特是用于符号。 当您左移31位,你得到的二进制补 ,这就是- (2 ^ 32)。 当您左移32位,这只是循环所有的方式回到身边1.如果你做到这一点移位与渴望,而不是整数,你会得到你所期望的答案(也就是直到你转移63+位)。
这里有一个微型基准长的情况下。 在我的笔记本电脑(2.8GHz的),用移而不是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
}