2009-12-07 31 views
22

Tengo código C en el que hago lo siguiente.Desplazamiento a la derecha de números negativos en C

int nPosVal = +0xFFFF; // + Added for ease of understanding 
int nNegVal = -0xFFFF; // - Added for valid reason 

Ahora cuando intento

printf ("%d %d", nPosVal >> 1, nNegVal >> 1); 

consigo

32767 -32768 

¿Esto es normal?

soy capaz de pensar en algo así como

65535 >> 1 = (int) 32767.5 = 32767 
-65535 >> 1 = (int) -32767.5 = -32768 

Es decir, -32767.5 se redondea a -32768.

¿Es correcto este entendimiento?

Respuesta

32

parece que su aplicación es, probablemente, haciendo un desplazamiento de bits aritméticas con números en complemento de dos. En este sistema, desplaza todos los bits hacia la derecha y luego rellena los bits superiores con una copia de lo que fue el último bit. Así, por tu ejemplo, int tratar como 32 bits aquí:

nPosVal = 00000000000000001111111111111111 
nNegVal = 11111111111111110000000000000001 

Después del cambio, tienes:

nPosVal = 00000000000000000111111111111111 
nNegVal = 11111111111111111000000000000000 

Si convierte esto de nuevo a decimal, se obtiene 32767 y -32768 respectivamente.

Efectivamente, un cambio a la derecha redondea hacia el infinito negativo.

Editar: De acuerdo con la Sección 6.5.7 de la última draft standard, este comportamiento en números negativos es dependiente de la implementación:

El resultado de E1 E2 es E1 >> posiciones de bits de derecha desplazado E2. Si E1 tiene un tipo sin signo o si E1 tiene un tipo firmado y un valor no negativo, el valor del resultado es la parte integral del cociente de E1/2 E2. Si E1 tiene un tipo firmado y un valor negativo, el valor resultante está definido por la implementación.

Su declarada rational para esto:

El Comité C89 afirmó la libertad en la ejecución otorgada por K & R en que no requiere la operación de desplazamiento a la derecha firmado a firmar extender, ya que este requisito podría reducir la velocidad de código rápido y desde la utilidad de firmar cambios extendidos es marginal. (El cambio de un número entero negativo dos complemento aritméticamente correcta lugar es no lo mismo que dividir por dos!)

Así que es dependiente de la implementación en la teoría. En la práctica, nunca he visto una implementación no hacer un desplazamiento aritmético a la derecha cuando el operando izquierdo está firmado.

+0

+1, eso es lo que quería saber. El cambio a la derecha gira hacia el infinito negativo. Pero está documentado? – Alphaneo

+0

Depende de la implementación. (Ver mi edición anterior.) Como dije, nunca he visto una implementación diferente en esto, pero teóricamente podría. – Boojum

2

Cuando se desplaza hacia la derecha, se descarta el bit menos significativo.

0xFFFF = 0 1111 1111 1111 1111, que derecho-turnos para dar 0 0,111 1,111 1,111 1,111 = 0x7FFF

-0xFFFF = 1 0000 0000 0000 0001 (complemento a 2), que derecho-turnos a 1 1.000 0.000 0000 0000 = -0x8000

7

La especificación C no especifica si el bit de signo está desplazado o no. Depende de la implementación.

+0

No creo que ese sea su problema ... – rlbond

+0

Q-1 preguntó si se esperaba el resultado. Mi respuesta sugiere que no, no puede esperar ningún resultado dado para el desplazamiento correcto del número negativo sin antes consultar la documentación de los compiladores – Trent

+0

¿Puede darme un enlace, donde el estándar lo dice? – hirschhornsalz

3

A-1: ​​Sí. 0xffff >> 1 es 0x7fff o 32767. No estoy seguro de lo que hace -0xffff. Eso es peculiar.

A-2: Cambiar no es lo mismo que dividir. Se trata de un cambio de bit, una operación binaria primitiva. Que a veces se puede usar para algunos tipos de división es conveniente, pero no siempre lo mismo.

+0

Los literales enteros son firmados, por lo que '- 0xFFFF' niega' 0xFFFF'. Es decir, es igual a '(~ 0xFFFF) -1', interpretado como un entero con signo. – outis

18

No, no obtiene números fraccionarios como 0.5 cuando se trabaja con enteros. Los resultados se pueden explicar fácilmente cuando nos fijamos en las representaciones binarias de los dos números:

 65535: 00000000000000001111111111111111 
    -65535: 11111111111111110000000000000001 

desplazamiento de bits a la derecha un poco, y se extienden a la izquierda (tenga en cuenta que esto depende de la implementación, gracias Trent):

65535 >> 1: 00000000000000000111111111111111 
-65535 >> 1: 11111111111111111000000000000000 

convertir de nuevo a decimal:

65535 >> 1 = 32767 
-65535 >> 1 = -32768 
+6

Tenga en cuenta que extenderse a la izquierda depende de la implementación. – Trent

+2

El estándar dice: "Si el valor del operando derecho es negativo o es mayor o igual que el ancho del operando izquierdo promocionado, el comportamiento no está definido". – Gonzalo

+0

@Trent: ¿estás seguro? Pensé que la extensión del signo depende de la firma del operando de la izquierda. – hirschhornsalz

2

por debajo del nivel C, las máquinas tienen un núcleo de CPU que es enteramente número entero o escalar. Aunque en la actualidad cada CPU de escritorio tiene una FPU, este no siempre era el caso, e incluso hoy en día los sistemas integrados se hacen sin instrucciones de coma flotante.

Los paradigmas de programación de hoy y los diseños de CPU e idiomas datan de la época en que la FPU podría no existir.

Así, instrucciones de la CPU implementan operaciones de punto fijo, tratados generalmente como puramente número entero ops. Solo si un programa declara elementos de float o double existirán fracciones. (Bueno, puede usar las operaciones de CPU para "punto fijo" con fracciones, pero eso es ahora y siempre fue bastante raro.)

Independientemente de lo que requería un comité de estándares de lenguaje hace años, todas las máquinas razonables propagan el signo poco en los cambios a la derecha de los números con signo. Los cambios a la derecha de los valores sin signo cambian en ceros a la izquierda. Los bits desplazados a la derecha se dejan caer al suelo.

Para ampliar su comprensión, deberá investigar "aritmética de dos dígitos".

+0

Creo que su definición de "máquina razonable" es estrecha. – Trent

+0

Si mi definición es limitada, por favor nombre una sola máquina para la cual 'x >> 1', en C, convertirá un número negativo en uno positivo. – DigitalRoss

+0

El compilador Microchip C18 (un enlace a la guía del usuario se puede encontrar aquí: http://tinyurl.com/ybt2svs - ver la sección B.4) – Trent

Cuestiones relacionadas