(int)Math.pow(2,x)和1 << x的Java结果不同
为什么以下两个操作在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
仅使用1 << x
的最低5位 。 因此,根据定义,1 << 32
与1 << 0
相同。 - 对左操作数的值的二进制补码整数表示执行移位操作 ; 这解释了为什么
1 << 31
是否定的。 -
Math.pow(2, 32)
返回一个double
。 -
(int)(d)
,其中d
是大于2147483647
的double
返回2147483647
( “int
类型的最大可表示值” )。
这个采访问题的作用是表明(int)Math.pow(2, x)
和1 << x
不等于0
... 30
范围之外的x
值。
PS或许有趣的是,使用long
代替int
(和1L
代替1
)将给出另一组与其他两个不同的结果。 即使最终结果转换为int
这也成立。
根据文档, Math.pow
会将其两个参数提升为double并返回double。 显然当返回的结果是double并且你将它转换为int时,你只得到最高的32位,其余的将被截断 – 因此你总是得到(int) Math.pow(2,x);
值。 当你进行bithift时,你总是使用整数,因此会发生溢出。
考虑int类型的限制。 它能保持多大的数量?
int的大小为32位,由于它是有符号的(默认情况下),因此第一位用于符号。 当你向左移31位时,你会得到两个恭维 ,即 – (2 ^ 32)。 当你向左移动32位时,它只是一直循环回到1.如果你用long而不是int来做这个移动,你会得到你期望的答案(直到你移动63+位)。
这是一个长期案例的微观基准。 在我的笔记本电脑(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 }