A mudança de bits depende da continuidade?

156

Suponha que eu tenha o número 'numb'=1025 [00000000 00000000 00000100 00000001]representado:

Na máquina Little-Endian:

00000001 00000100 00000000 00000000

Na máquina Big-Endian:

00000000 00000000 00000100 00000001

Agora, se eu aplicar o Shift Esquerdo em 10 bits (ou seja: numb << = 10), devo ter:

[A] Na máquina Little-Endian:

Como observei no GDB, Little Endian faz a mudança para a esquerda em 3 etapas: [Eu mostrei '3' Steps para entender melhor apenas o processamento]

  1. Trate o não. na Convenção Big-Endian:

    00000000        00000000        00000100    00000001
  2. Aplicar Shift à esquerda:

    00000000        00010000        00000100        00000000
  3. Represente o resultado novamente em Little-Endian:

    00000000        00000100        00010000        00000000 

[B] Na máquina Big-Endian:

00000000        00010000        00000100        00000000

Minha pergunta é:

Se eu aplicar diretamente uma mudança à esquerda na Convenção Little Endian, ela deverá fornecer:

numb:

00000001 00000100 00000000 00000000

numb << 10:

00010000 00000000 00000000 00000000

Mas, na verdade, fornece:

00000000        00000100        00010000        00000000 

Para alcançar apenas o segundo resultado, mostrei três etapas hipotéticas acima.

Por favor, explique-me por que os dois resultados acima são diferentes: O resultado real de numb << 10é diferente do resultado esperado.

Sandeep Singh
fonte

Respostas:

194

Endianness é a maneira como os valores são armazenados na memória. Quando carregada no processador, independentemente da resistência, a instrução de troca de bits está operando com o valor no registro do processador. Portanto, carregar da memória para o processador equivale à conversão para big endian, a operação de deslocamento vem a seguir e o novo valor é armazenado na memória, que é onde a pequena ordem de bytes endian entra em vigor novamente.

Atualização, graças a @jww: No PowerPC, o vetor muda e gira é sensível a endian. Você pode ter um valor em um registro de vetor e uma mudança produzirá resultados diferentes em little-endian e big-endian .

Carl
fonte
4
Obrigada pelo esclarecimento. Você pode sugerir alguma referência em que eu possa entender melhor esses meandros.
Sandeep Singh
4
A melhor coisa para entender o endianness é realmente usá-lo em diferentes arquiteturas em um nível incorporado. No entanto, eu poderia encaminhá-lo para esses dois artigos: codeproject.com/KB/cpp/endianness.aspx e ibm.com/developerworks/aix/library/au-endianc/…
Carl
3
Então, meu código funcionará independentemente do endian ?! isso é ótimo! Eu estava tão preocupado que eu teria que hackear meu código para o inferno e voltar!
MarcusJ
2
@ MarcusJ: Não necessariamente. Por exemplo, se você estiver lendo 4 bytes de um arquivo que representa um número inteiro de 32 bits, considere a persistência dos dados que está lendo em conjunto com a persistência do sistema que recebe os dados para interpretar adequadamente os dados.
Carl
3
No PowerPC, o vetor muda e gira é sensível a endian. Você pode ter um valor em um registro de vetor e uma mudança produzirá resultados diferentes em little-endian e big-endian.
JWW
58

Não, o deslocamento de bits, como qualquer outra parte de C, é definido em termos de valores , não de representações. O desvio à esquerda por 1 é a multiplicação por 2, o desvio à direita é a divisão. (Como sempre, ao usar operações bit a bit, tenha cuidado com a assinatura. Tudo está mais bem definido para tipos integrais não assinados.)

Kerrek SB
fonte
1
Isso é basicamente verdadeiro para aritmética inteira, mas C fornece muitos casos de comportamento dependente de representação.
Edmund
2
@ Edmund: Hm ... principalmente a implementação da assinatura não é especificada e, como conseqüência, o comportamento das operações bit a bit (como deslocamento à direita) e do módulo e da divisão são implementadas definidas em números inteiros negativos. Que outras coisas você tem em mente que são definidas pela implementação?
Kerrek SB
Infelizmente, o @KerrekSB não está definido como implementação em números inteiros negativos. Eles não são especificados no C89 e indefinidos no C99 +, o que foi uma péssima ideia.
Paolo Bonzini 01/01
@PaoloBonzini: Sim, bom ponto. Na verdade, isso é ainda melhor, pois reforça o ponto de que as operações de turno são definidas em termos de valores, possivelmente indefinidas quando o resultado não é representável, e especular sobre a representação subjacente não ajuda.
Kerrek SB 01/01
@KerrekSB: o fato é que todos realmente precisam que o turno esquerdo seja representado como valores e como representação, dependendo do caso. E o uso de números inteiros não assinados pode causar outros problemas, por exemplo x &= -1u << 20, provavelmente estará incorreto se xtiver 64 bits e int32 bits. Por esse motivo, o GCC promete nunca tratar os turnos assinados como indefinidos ou mesmo não especificados.
Paolo Bonzini
5

Qualquer instrução de deslocamento que desloque primeiro os bits de ordem superior é considerada o deslocamento esquerdo. Qualquer instrução de mudança que desloque primeiro os bits de ordem inferior é considerada a mudança certa. Nesse sentido, o comportamento de >>e <<para os unsignednúmeros não dependerá da persistência.

Davislor
fonte
4

Os computadores não escrevem números da maneira que fazemos. O valor simplesmente muda. Se você insistir em examiná-lo byte a byte (mesmo que não seja assim que o computador faz), você poderia dizer que em uma máquina little-endian, o primeiro byte muda para a esquerda, o excesso de bits entra no segundo byte, e assim por diante.

(A propósito, little-endian faz mais sentido se você escrever os bytes verticalmente e não horizontalmente, com endereços mais altos no topo. O que acontece é como os diagramas de mapas de memória são comumente desenhados.)

Raymond Chen
fonte
2

Embora a resposta aceita indique que endianess é um conceito da visão da memória. Mas não acho que isso responda diretamente à pergunta.

Algumas respostas me dizem que as operações bit a bit não dependem de endianess , e o processador pode representar os bytes de qualquer outra maneira. De qualquer forma, está falando que endianess é abstraída.

Mas quando fazemos alguns cálculos bit a bit no papel, por exemplo, não precisamos declarar a endianess em primeiro lugar? Na maioria das vezes escolhemos implicitamente uma endianess.

Por exemplo, suponha que temos uma linha de código como esta

0x1F & 0xEF

Como você calcularia o resultado manualmente, em um papel?

  MSB   0001 1111  LSB
        1110 1111
result: 0000 1111

Então, aqui usamos um formato Big Endian para fazer o cálculo. Você também pode usar Little Endian para calcular e obter o mesmo resultado.

Aliás, quando escrevemos números em código, acho que é como um formato Big Endian. 123456ou 0x1F, os números mais significativos começam da esquerda.

Novamente, assim que escrevemos um formato binário de um valor no papel, acho que já escolhemos uma Endianess e estamos vendo o valor como o vemos na memória.

Então, voltando à questão, uma operação de mudança <<deve ser pensada como uma mudança de LSB (byte menos significativo) para MSB (byte mais significativo) .

Então, como no exemplo da pergunta:

numb=1025

Pequeno endian

LSB 00000001 00000100 00000000 00000000 MSB

Então, << 10seria 10bitmudar de LSB para MSB.


Comparação e << 10operações para o formato Little Endian passo a passo:

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!)

Uau! Eu recebo o resultado esperado conforme o OP descrito!

Os problemas que o OP não obteve o resultado esperado são:

  1. Parece que ele não mudou do LSB para o MSB.

  2. Ao mudar bits no formato Little Endian, você deve perceber (graças a Deus eu entendi) que:

LSB 10000000 00000000 MSB << 1é
LSB 00000000 00000001 MSB, não LSB 01000000 00000000 MSB

Porque para cada indivíduo 8bits, na verdade, estamos escrevendo em um MSB 00000000 LSBformato Big Endian.

Então é como

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


Resumindo:

  1. Embora as operações bit a bit sejam abstraídas blablablabla ..., quando calculamos as operações bit a bit manualmente, ainda precisamos saber qual endianess estamos usando ao escrever o formato binário no papel. Também precisamos garantir que todos os operadores usem a mesma resistência.

  2. O OP não obteve o resultado esperado porque ele fez a mudança errada.

Rick
fonte