2011-07-27 23 views
9

Estoy implementando decodificación de enteros BER-comprimidos y recientemente he encontrado un extraño comportamiento de JavaScript relacionado con operaciones bit a bit con enteros grandes.Operaciones bit a bit con enteros grandes

ej .:

var a = 17516032;   // has 25 bits 
alert(a << 7)    // outputs -2052915200 
alert(a * 128)    // outputs 2242052096 
alert(2242052096 >> 16) // outputs -31325 
alert(2242052096/65536) // outputs 34211 

Mientras que la primera solución (multiplicación en lugar de desplazamiento a la izquierda) es aceptable, el segundo no lo es.

¿Por qué sucede? ¿Cómo soportarlo?

+0

¿No entiendo por qué la división no sería aceptable? – Guffa

+0

@Guffa Necesito un enfoque general, no solo para enteros de 32 bits. –

+0

La división no se realiza en enteros, se realiza en números de punto flotante, por lo que no entiendo su argumento. Además, un número de punto flotante de precisión doble solo puede representar un número entero de 53 bits, por lo que no es mucho si quiere decodificar números comprimidos en BER. – Guffa

Respuesta

7

17516032 en binario es 00000001000010110100011000000000. Si se desplaza hacia la izquierda en 7, obtendrá 10000101101000110000000000000000. Esto es igual a -2052915200 en two's complement (que es como casi todas las computadoras representan números negativos).

>> es un cambio a la derecha con signo. Eso significa que el bit más a la izquierda (que determina el signo de un número) se desplazará hacia el lado izquierdo.

p. Ej.

1100 >> 2 == 1111 
0111 >> 2 == 0001 

Si usted quiere hacer un cambio sin signo (que ignora el bit de signo), utilice >>> que un relleno de cero en el extremo izquierdo de la cadena de bits.

+0

¡Eso es todo, gracias! ¿Alguna idea de por qué el cambio a la izquierda produce un resultado tan extraño? –

+0

No hay problema. Edité mi respuesta para incluir eso. – tskuzzy

3

Los operadores bit a bit trabajan en enteros de 32 bits, mientras que la multiplicación y división funcionan en números de coma flotante.

Cuando se cambia un número, se convierte de un número de coma flotante a un entero de 32 bits antes de las operaciones, y se convierte de nuevo a un número de coma flotante después de la operación. El número 2242052096 tiene el bit 32 establecido, por lo que es un número negativo cuando se convierte desde y hacia un entero de 32 bits.

El operador de desplazamiento a la derecha >> no cambia el signo del valor, es decir, los bits que se desplazan desde la izquierda tienen el mismo valor que el bit de signo. Use el operador de desplazamiento a la derecha >>> para cambiar en cero bits en su lugar.

Referencia: MDN: Bitwise operators

2

(2242052096/65536) == (2242052096 >>> 16)

Nota los diferentes turnos.

1

Normalmente, Javascript representa los números como (punto flotante de doble precisión).

Casi todas las operaciones en modo bit se convierten en un entero de 32 bits con signo, hacen lo que sea que vayan a hacer, luego tratan el resultado como un entero de 32 bits con signo al convertir de nuevo.

La excepción es >>> que trata el resultado como sin signo entero de 32 bits al convertir de nuevo.

Así:

  • desplazamientos a la derecha se puede hacer para trabajar simplemente usando >>> en lugar de >>;
  • a * 128 da la respuesta esperada porque nunca se convierte en un entero de 32 bits con signo, es solo una multiplicación de coma flotante;
  • a << 7 da una respuesta inesperada porque se convierte en un entero de 32 bits con signo, y luego cambia un 1 en el bit de signo, lo que da como resultado un valor negativo de 32 bits con signo.

no hay una <<<, pero si usted quiere escribir su desplazamiento a la izquierda como un cambio, puede utilizar

(a << 7) >>> 0 

para obtener la respuesta esperada (la >>> 0 eficazmente arroja el firmado 32- valor de bit a un valor de 32 bits sin signo).

+0

Agradable. ¡Gracias por la explicación! –

Cuestiones relacionadas