¿El cambio de bit depende de la resistencia?

156

Supongamos que tengo el número 'numb'=1025 [00000000 00000000 00000100 00000001]representado:

En la máquina Little-Endian:

00000001 00000100 00000000 00000000

En la máquina Big-Endian:

00000000 00000000 00000100 00000001

Ahora, si aplico el Desplazamiento a la izquierda en 10 bits (es decir: entumecido << = 10), debería tener:

[A] En la máquina Little-Endian:

Como noté en GDB, Little Endian realiza el Desplazamiento a la izquierda en 3 pasos: [He mostrado '3' Pasos para comprender mejor solo el procesamiento]

  1. Trata el no. en la Convención Big-Endian:

    00000000        00000000        00000100    00000001
  2. Aplicar desplazamiento a la izquierda:

    00000000        00010000        00000100        00000000
  3. Representa el resultado nuevamente en Little-Endian:

    00000000        00000100        00010000        00000000 

[SI]. En la máquina Big-Endian:

00000000        00010000        00000100        00000000

Mi pregunta es:

Si aplico directamente un Desplazamiento a la izquierda en la Convención Little Endian, debería dar:

numb:

00000001 00000100 00000000 00000000

numb << 10:

00010000 00000000 00000000 00000000

Pero en realidad, da:

00000000        00000100        00010000        00000000 

Para lograr el segundo resultado solamente, he mostrado tres pasos hipotéticos arriba.

Explíqueme por qué los dos resultados anteriores son diferentes: el resultado real de numb << 10es diferente del resultado esperado.

Sandeep Singh
fuente

Respuestas:

194

Endianness es la forma en que los valores se almacenan en la memoria. Cuando se carga en el procesador, independientemente de la duración, la instrucción de cambio de bit está operando en el valor en el registro del procesador. Por lo tanto, la carga de la memoria al procesador es el equivalente de la conversión a Big Endian, la operación de desplazamiento viene a continuación y luego el nuevo valor se almacena de nuevo en la memoria, que es donde el orden de bytes de Little Endian entra en vigencia nuevamente.

Actualización, gracias a @jww: en PowerPC, los cambios y rotaciones de vectores son sensibles al endian. Puede tener un valor en un registro vectorial y un cambio producirá resultados diferentes en little-endian y big-endian .

Carl
fuente
44
Gracias por la explicación. ¿Puede sugerirme alguna referencia donde pueda obtener una mejor comprensión de tales complejidades?
Sandeep Singh
44
Lo mejor para comprender la endianidad es usarla realmente en diferentes arquitecturas a un nivel integrado. Sin embargo, podría referirlo a estos dos artículos: codeproject.com/KB/cpp/endianness.aspx e ibm.com/developerworks/aix/library/au-endianc/…
Carl
3
¿Entonces mi código funcionará independientemente de endian? ¡esto es genial! ¡He estado TAN preocupado de tener que hackear mi código al infierno y viceversa!
MarcusJ
2
@MarcusJ: No necesariamente. Por ejemplo, si está leyendo 4 bytes de un archivo que representa un número entero de 32 bits, debe tener en cuenta la endianness de los datos que está leyendo junto con la endianness del sistema que recibe los datos para interpretar correctamente los datos.
Carl
3
En PowerPC, los desplazamientos y rotaciones del vector son sensibles al endian. Puede tener un valor en un registro vectorial y un cambio producirá resultados diferentes en little-endian y big-endian.
jww
58

No, el desplazamiento de bits, como cualquier otra parte de C, se define en términos de valores , no representaciones. El desplazamiento a la izquierda por 1 es la multiplicación por 2, el desplazamiento a la derecha es la división. (Como siempre cuando se utilizan operaciones bit a bit, tenga cuidado con la firma. Todo está más bien definido para los tipos integrales sin signo).

Kerrek SB
fuente
1
Esto es básicamente cierto para la aritmética de enteros, pero C proporciona muchos casos de comportamiento dependiente de la representación.
Edmund
2
@Edmund: Hm ... lo más notable es que la implementación de la firma no está especificada, y como consecuencia el comportamiento de las operaciones bit a bit (como el desplazamiento a la derecha) y el módulo y la división son implementaciones definidas en enteros negativos. ¿Qué otras cosas tiene en mente que están definidas en la implementación?
Kerrek SB
@KerrekSB desafortunadamente no son implementaciones definidas en enteros negativos. No están especificados en C89 y no están definidos en C99 +, lo cual fue una muy mala idea.
Paolo Bonzini
@PaoloBonzini: Sí, buen punto. En realidad, eso es aún mejor, ya que refuerza el punto de que las operaciones de cambio se definen en términos de valores, posiblemente sin definir cuando el resultado no es representable, y que especular sobre la representación subyacente no ayuda.
Kerrek SB
@KerrekSB: la cuestión es que todos realmente necesitan desplazamiento a la izquierda para ser representados tanto como valores como representación, según el caso. Y el uso de enteros sin signo puede causar otros problemas, por ejemplo, x &= -1u << 20lo más probable es que sea incorrecto si xes de 64 bits y de int32 bits. Por esta razón, GCC promete nunca tratar los turnos firmados como indefinidos o incluso no especificados.
Paolo Bonzini el
5

Cualquier instrucción de desplazamiento desplaza primero los bits de orden superior se considera desplazamiento a la izquierda. Cualquier instrucción de desplazamiento desplaza primero los bits de orden inferior se considera el desplazamiento correcto. En ese sentido, el comportamiento de >>y <<para los unsignednúmeros no dependerá de la resistencia.

Davislor
fuente
4

Las computadoras no escriben números como nosotros. El valor simplemente cambia. Si insiste en mirarlo byte por byte (aunque no es así como lo hace la computadora), podría decir que en una máquina little-endian, el primer byte se desplaza hacia la izquierda, el exceso de bits va al segundo byte, y así.

(Por cierto, little-endian tiene más sentido si escribe los bytes verticalmente en lugar de horizontalmente, con direcciones más altas en la parte superior. Lo que sucede es cómo se dibujan comúnmente los diagramas de mapas de memoria).

Raymond Chen
fuente
2

Aunque la respuesta aceptada señala que la resistencia es un concepto desde el punto de vista de la memoria. Pero no creo que eso responda la pregunta directamente.

Algunas respuestas me dicen que las operaciones bit a bit no dependen de la resistencia , y el procesador puede representar los bytes de cualquier otra manera. De todos modos, se habla de que la endianess se abstrae.

Pero cuando hacemos algunos cálculos bit a bit en el papel, por ejemplo, ¿no es necesario indicar la resistencia en primer lugar? La mayoría de las veces elegimos un endianess implícitamente.

Por ejemplo, supongamos que tenemos una línea de código como esta

0x1F & 0xEF

¿Cómo calcularías el resultado a mano, en un papel?

  MSB   0001 1111  LSB
        1110 1111
result: 0000 1111

Así que aquí usamos un formato Big Endian para hacer el cálculo. También puede usar Little Endian para calcular y obtener el mismo resultado.

Por cierto, cuando escribimos números en código, creo que es como un formato Big Endian. 123456o 0x1F, los números más significativos comienzan desde la izquierda.

Nuevamente, tan pronto como escribimos un formato binario de un valor en el papel, creo que ya elegimos un Endianess y estamos viendo el valor tal como lo vemos desde la memoria.

Volviendo a la pregunta, una operación de cambio <<debe considerarse como un cambio de LSB (byte menos significativo) a MSB (byte más significativo) .

Entonces, como para el ejemplo en la pregunta:

numb=1025

Little Endian

LSB 00000001 00000100 00000000 00000000 MSB

Entonces << 10estaría 10bitcambiando de LSB a MSB.


Comparación y << 10operaciones para el formato Little Endian paso a paso:

MSB                                        LSB
    00000000  00000000  00000100  00000001  numb(1025)
    00000000  00010000  00000100  00000000  << 10

LSB                                        MSB
    00000000  00000100  00010000  00000000 numb(1025) << 10, and put in a Little Endian Format

LSB                                        MSB
    00000001  00000100  00000000  00000000 numb(1205) in Little Endian format
    00000010  00001000  00000000  00000000 << 1 
    00000100  00010000  00000000  00000000 << 2 
    00001000  00100000  00000000  00000000 << 3 
    00010000  01000000  00000000  00000000 << 4
    00100000  10000000  00000000  00000000 << 5
    01000000  00000000  00000001  00000000 << 6
    10000000  00000000  00000010  00000000 << 7
    00000000  00000001  00000100  00000000 << 8
    00000000  00000010  00001000  00000000 << 9
    00000000  00000100  00010000  00000000 << 10 (check this final result!)

¡Guauu! ¡Obtuve el resultado esperado como lo describió el OP!

Los problemas que el OP no obtuvo el resultado esperado son que:

  1. Parece que no cambió de LSB a MSB.

  2. Al cambiar bits en formato Little Endian, debes darte cuenta (gracias a Dios, me doy cuenta) de que:

LSB 10000000 00000000 MSB << 1es
LSB 00000000 00000001 MSB, no LSB 01000000 00000000 MSB

Porque para cada individuo 8bits, en realidad lo estamos escribiendo en MSB 00000000 LSBformato Big Endian.

Entonces es como

LSB[ (MSB 10000000 LSB) (MSB 00000000 LSB) ]MSB


Para resumir:

  1. Aunque se dice que las operaciones bit a bit se abstraen de blablablabla ..., cuando calculamos las operaciones bit a mano, todavía necesitamos saber qué endianess estamos utilizando a medida que escribimos el formato binario en el papel. También debemos asegurarnos de que todos los operadores usen la misma resistencia.

  2. El OP no obtuvo el resultado esperado porque hizo mal el cambio.

Almiar
fuente