(int)Math.pow(2,x)和1 << x的Java结果不同

为什么以下两个操作在Java中为x = 3132产生不同的结果,但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只能存储-21474836482147483647之间的值。
  • 1 << x仅使用 1 << x 的最低5位 。 因此,根据定义, 1 << 321 << 0相同。
  • 对左操作数的值的二进制补码整数表示执行移位操作 ; 这解释了为什么1 << 31是否定的。
  • Math.pow(2, 32)返回一个double
  • (int)(d) ,其中d是大于2147483647double返回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 }