비트 시프트는 엔디안에 의존합니까?


156

내가 숫자를 'numb'=1025 [00000000 00000000 00000100 00000001]표현 했다고 가정 해보십시오 .

리틀 엔디 언 머신에서 :

00000001 00000100 00000000 00000000

빅 엔디안 머신에서 :

00000000 00000000 00000100 00000001

이제 10 비트에 왼쪽 시프트를 적용하면 (즉 : numb << = 10) 다음을 가져야합니다.

[A] Little-Endian 머신에서 :

GDB에서 알 수 있듯이 Little Endian은 왼쪽 이동을 3 단계로 수행합니다.

  1. 아니오를 취급하십시오. 빅 엔디안 컨벤션에서 :

    00000000        00000000        00000100    00000001
  2. 왼쪽-시프트 적용 :

    00000000        00010000        00000100        00000000
  3. Little-Endian에서 결과를 다시 나타냅니다.

    00000000        00000100        00010000        00000000 

[비]. 빅 엔디안 머신에서 :

00000000        00010000        00000100        00000000

내 질문은 :

리틀 엔디안 컨벤션에 직접 왼쪽 쉬프트를 적용하면 다음과 같이됩니다.

numb:

00000001 00000100 00000000 00000000

numb << 10:

00010000 00000000 00000000 00000000

그러나 실제로는 다음을 제공합니다.

00000000        00000100        00010000        00000000 

두 번째 결과 만 달성하기 위해 위의 세 가지 가상 단계를 보여주었습니다.

위의 두 결과가 다른 이유를 설명해주십시오. 실제 결과 numb << 10가 예상 결과와 다릅니다.

답변:


194

엔디안은 값이 메모리에 저장되는 방식입니다. 엔디안과 상관없이 프로세서에로드 될 때 비트 시프트 명령은 프로세서 레지스터의 값에서 작동합니다. 따라서, 메모리에서 프로세서로의 로딩은 빅 엔디안으로 변환하는 것과 동일하며, 시프 팅 동작은 다음에오고 새로운 값은 메모리에 다시 저장되는데, 여기서 리틀 엔디안 바이트 순서가 다시 적용됩니다.

@jww 덕분에 업데이트 : PowerPC에서 벡터 이동 및 회전은 엔디안에 민감합니다. 벡터 레지스터에 값을 가질 수 있으며 시프트 는 little-endian 및 big-endian에서 다른 결과를 생성 합니다.


4
설명 주셔서 감사합니다. 그러한 복잡성을 더 잘 이해할 수있는 곳을 참조 해 주시겠습니까?
Sandeep Singh

4
엔디안을 이해하기위한 최선의 방법은 임베디드 수준에서 다른 아키텍처에서 실제로 사용하는 것입니다. 그러나 codeproject.com/KB/cpp/endianness.aspxibm.com/developerworks/aix/library/au-endianc/…
Carl

3
내 코드는 엔디안에 관계없이 작동합니까?! 대단해! 나는 내 코드를 해킹하고 다시 죽여야 할까봐 걱정했다!
MarcusJ

2
@MarcusJ : 반드시 그런 것은 아닙니다. 예를 들어, 32 비트 정수를 나타내는 파일에서 4 바이트를 읽는 경우 올바르게 해석하려면 데이터를 수신하는 시스템의 엔디안과 함께 읽는 데이터의 엔디안을 고려해야합니다. 자료.
Carl

3
PowerPC에서 벡터 이동 및 회전은 엔디안에 민감합니다. 벡터 레지스터에 값을 가질 수 있으며 시프트는 리틀 엔디안 및 빅 엔디안에서 다른 결과를 생성합니다.
jww

58

C의 다른 부분과 마찬가지로 비트 시프트는 표현이 아닌 값으로 정의됩니다 . 1만큼 왼쪽 이동은 2로 곱셈되고 오른쪽 이동은 나눗셈입니다. (비트 단위 연산을 사용할 때와 마찬가지로 부호를주의하십시오. 모든 것이 부호없는 정수 유형에 대해 가장 잘 정의되어 있습니다.)


1
이것은 정수 산술의 경우 기본적으로 사실이지만 C는 표현에 의존하는 많은 경우를 제공합니다.
Edmund

2
@Edmund : 흠 ... 가장 두드러진 부호의 구현은 명시되어 있지 않기 때문에 비트 연산 (오른쪽 시프트와 같은)과 모듈로와 나누기의 동작은 음의 정수로 정의 된 구현입니다. 구현 정의 된 다른 사항은 무엇입니까?
Kerrek SB

@KerrekSB 불행히도 그들은 음의 정수로 정의 된 구현이 아닙니다. 그들은 C89에서 지정되지 않았고 C99 +에서 정의되지 않았습니다. 이것은 매우 나쁜 생각이었습니다.
Paolo Bonzini 2016 년

@PaoloBonzini : 예, 좋습니다. 실제로 시프트 연산이 값 측면에서 정의되고 결과를 표현할 수 없을 때 정의되지 않을 수 있으며 기본 표현에 대한 추측이 도움이되지 않는다는 점을 강조하기 때문에 훨씬 더 좋습니다.
Kerrek SB

@ KerrekSB : 사건에 따라 모든 사람이 실제로 가치와 표현으로 표현하기 위해 왼쪽 교대가 필요하다는 것입니다. 부호없는 정수를 사용하면 다른 문제가 발생할 수 있습니다. 예를 들어 64 비트이고 32 비트 인 x &= -1u << 20경우 올바르지 않을 수 있습니다 . 이러한 이유로 GCC는 서명 된 시프트를 정의되지 않거나 지정되지 않은 것으로 취급하지 않을 것을 약속합니다. xint
Paolo Bonzini

5

어떤 시프트 명령이 더 높은 비트를 먼저 시프트하는지는 왼쪽 시프트로 간주됩니다. 어떤 시프트 명령이 하위 비트를 먼저 시프트 시키는가 올바른 시프트로 간주됩니다. 그런 의미에서의 동작 >><<에 대한 unsigned번호 엔디안에 의존하지 않습니다.


4

컴퓨터는 우리가하는 방식대로 숫자를 쓰지 않습니다. 값은 단순히 이동합니다. 바이트 단위로 보겠다고 주장하면 (컴퓨터가 그렇게하는 방식은 아니지만) 리틀 엔디안 컴퓨터에서 첫 번째 바이트가 왼쪽으로 이동하고 초과 비트는 두 번째 바이트로 이동한다고 말할 수 있습니다. 등등.

(반드시, 리틀 엔디안은 더 높은 주소를 가진 수평이 아닌 수직으로 바이트를 쓰면 더 의미가 있습니다. 메모리 맵 다이어그램이 일반적으로 그려지는 방식입니다.)


2

수용 된 대답은 엔디안이 메모리 관점의 개념이라고 지적합니다. 그러나 나는 그것이 그 질문에 직접 대답한다고 생각하지 않습니다.

일부 답변에 따르면 비트 단위 연산은 endianess에 의존하지 않으며 프로세서는 다른 방식으로 바이트를 나타낼 수 있습니다. 어쨌든, 그것은 엔디안이 추상화되는 것에 대해 이야기하고 있습니다.

그러나 예를 들어 종이에서 비트 단위로 계산할 때 엔디안을 먼저 언급 할 필요가 없습니까? 대부분의 경우 우리는 내재적으로 엔디안을 선택합니다.

예를 들어, 이와 같은 코드가 있다고 가정

0x1F & 0xEF

종이로 손으로 결과를 어떻게 계산할까요?

  MSB   0001 1111  LSB
        1110 1111
result: 0000 1111

여기에서는 빅 엔디안 형식을 사용하여 계산을 수행합니다. Little Endian을 사용하여 동일한 결과를 계산하고 얻을 수도 있습니다.

Btw, 코드로 숫자를 쓸 때 빅 엔디안 형식과 같다고 생각합니다. 123456또는 0x1F가장 중요한 숫자는 왼쪽에서 시작합니다.

다시, 종이에 이진 형식의 값을 쓰 자마자 우리는 이미 Endianess를 선택했으며 메모리에서 볼 때 가치를보고 있다고 생각합니다.

다시 질문으로 돌아 가면, 시프트 연산 <<LSB (최하위 바이트)에서 MSB (최고 바이트)이동하는 것으로 생각해야합니다 .

그런 다음 질문의 예는 다음과 같습니다.

numb=1025

리틀 엔디안

LSB 00000001 00000100 00000000 00000000 MSB

그래서 << 10될 수 10bit에서 LSB MSB로 이동.


<< 10Little Endian 형식의 단계별 비교 및 조작 :

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

와! OP가 설명한대로 예상 결과를 얻습니다!

OP가 예상 한 결과를 얻지 못한 문제는 다음과 같습니다.

  1. 그가 LSB에서 MSB로 이동하지 않은 것 같습니다.

  2. 리틀 엔디안 형식으로 비트를 옮길 때 다음을 인식해야합니다.

LSB 10000000 00000000 MSB << 1있다
LSB 00000000 00000001 MSB, 없다 LSB 01000000 00000000 MSB

각 개인마다 8bits실제로 MSB 00000000 LSBBig Endian 형식으로 작성하기 때문 입니다.

그래서 그것은

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


요약하면 :

  1. 비트 단위 연산은 blablablabla에서 추상화되었다고 말하지만, 비트 단위 연산을 직접 계산할 때 이진 형식을 종이에 적을 때 사용하는 엔디안을 여전히 알아야합니다. 또한 모든 연산자가 동일한 엔디안을 사용하도록해야합니다.

  2. OP는 기대 한 결과를 얻지 못했습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.