2012-05-02 22 views
8

¿Por qué las dos operaciones siguientes arrojan resultados diferentes en Java para x = 31 o 32 pero los mismos resultados para x=3?Los resultados de Java son diferentes para (int) Math.pow (2, x) y 1 << x

int x=3; 
int b = (int) Math.pow(2,x); 
int c = 1<<x; 

Resultados:

x=32: b=2147483647; c=1; 
x=31: b=2147483647; c=-2147483648; 
x=3: b=8   ; c=8 
+1

Una sutil diferencia es que pow() es ** ** mucho más lenta, incluso si la respuesta es la misma. pow() tiene error de redondeo, mientras que 'int' tiene overflow. Puede intentar '1L << 32' que es igual a' 2147483648' –

Respuesta

18

hay múltiples cuestiones en juego:

Lo que hace esta pregunta entrevista es mostrar que (int)Math.pow(2, x) y 1 << x no son equivalentes a los valores de x fuera del rango 0 ... 30.

P.S. Es quizá interesante notar que el uso de long en lugar de int (y 1L en lugar de 1) daría otra serie de resultados diferentes de los otros dos. Esto se mantiene incluso si los resultados finales se convierten a int.

+2

Especialmente el último punto es muy importante. –

2

considerar los límites del tipo int. ¿Qué tan grande puede contener?

+0

La pregunta es acerca de la diferencia entre el desplazamiento a la izquierda y las potencias explícitas de 2. –

+1

Y agregue al límite de 'int' el hecho de que es un tipo firmado. –

3

De acuerdo con la documentación Math.pow promoverá tanto de sus argumentos para doblar y volver doble. Obviamente, cuando el resultado devuelto es el doble y se lo transfiere a int obtendrá solo los 32 bits más altos y el resto se truncará, por lo que siempre obtendrá el valor (int) Math.pow(2,x);. Cuando haces cambio de bits, siempre trabajas con enteros y, por lo tanto, se produce un desbordamiento.

+0

Supongo que los 32 bits más altos de la mantisa. Aunque podría estar equivocado. – asenovm

+0

Hm, está bien, eso podría ser cierto. Acabo de recordar que siempre devuelve '2^31-1'. Se ha eliminado el comentario :) –

0

int es de 32 bits de tamaño y puesto que está firmado (por defecto), el primer bit se usa para la señal. Cuando mueve 31 bits hacia la izquierda, obtiene el Two's Compliment, que es - (2^32). Cuando desplazas los 32 bits de la izquierda, simplemente gira alrededor de 1. Si hicieras este cambio con largos en lugar de con enteros, obtendrías las respuestas esperadas (es decir hasta que cambies 63+ bits).

0

Aquí hay una micro-referencia para el caso de un largo. En mi computadora portátil (2.8GHz), usar el cambio en lugar de Math.pow es más de 7 veces más rápido.

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 
} 
Cuestiones relacionadas