인텔 프로세서 (및 다른 프로세서)는 스토리지에 리틀 엔디안 형식을 사용합니다.
나는 왜 누군가가 바이트를 역순으로 저장하고 싶어하는지 궁금합니다. 이 형식은 빅 엔디안 형식보다 장점이 있습니까?
인텔 프로세서 (및 다른 프로세서)는 스토리지에 리틀 엔디안 형식을 사용합니다.
나는 왜 누군가가 바이트를 역순으로 저장하고 싶어하는지 궁금합니다. 이 형식은 빅 엔디안 형식보다 장점이 있습니까?
답변:
어느 쪽이든 논쟁이 있지만 한 가지 요점은 리틀 엔디안 시스템에서 32, 16 또는 8 비트 폭으로 취한 메모리의 주어진 값의 주소가 동일하다는 것입니다.
즉, 메모리에 2 바이트 값이있는 경우 :
0x00f0 16
0x00f1 0
'16'을 16 비트 값 (대부분의 32 비트 시스템에서 c '짧음') 또는 8 비트 값 (일반적으로 c 'char')으로 사용하면 가져 오는 주소가 아니라 사용하는 가져 오기 명령 만 변경됩니다. 에서.
빅 엔디안 시스템에서 위의 내용은 다음과 같습니다.
0x00f0 0
0x00f1 16
포인터를 증가시킨 다음 새 값에서 더 좁은 페치 조작을 수행해야합니다.
간단히 말해, '작은 엔디안 시스템에서는 캐스트가 작동하지 않습니다.'
나는 왜 누군가가 바이트를 역순으로 저장하고 싶어하는지 궁금합니다.
빅 엔디안과 리틀 엔디안은 인간의 관점에서 볼 때 "정상 질서"와 "역순"일뿐입니다.
그것들은 CPU에 전혀 중요하지 않은 모든 인간 관습입니다. # 1과 # 2를 유지하고 # 3을 뒤집 으면 리틀 엔디안은 오른쪽에서 왼쪽으로 쓰는 아랍어 나 히브리어를 읽는 사람들에게 "완벽하게 자연스러워"보일 것입니다.
그리고 빅 엔디안을 부 자연스럽게 보이게하는 다른 인간 협약이 있습니다 ...
필자가 68K와 PowerPC를 주로 프로그래밍 할 때 빅 엔디안은 "올 바르고", 리틀 엔디안은 "잘못됨"으로 간주했다. 그러나 더 많은 ARM 및 인텔 작업을 수행 한 이후로 리틀 엔디안에 익숙해졌습니다. 정말 중요하지 않습니다.
좋아, 여기에 나에게 설명했던 이유가 있습니다 : 덧셈과 뺄셈
멀티 바이트 숫자를 더하거나 빼는 경우 최하위 바이트로 시작해야합니다. 예를 들어 두 개의 16 비트 숫자를 추가하는 경우 최하위 바이트에서 최상위 바이트로의 캐리가있을 수 있으므로 캐리가 있는지 확인하려면 최하위 바이트로 시작해야합니다. 이것은 장기 덧셈을 할 때 가장 오른쪽 숫자로 시작하는 것과 같은 이유입니다. 왼쪽부터 시작할 수 없습니다.
메모리에서 바이트를 순차적으로 페치하는 8 비트 시스템을 고려하십시오. 최하위 바이트를 먼저 페치하면 최상위 바이트가 메모리에서 페치되는 동안 추가 를 시작할 수 있습니다 . 이러한 병렬성은 시스템과 같은 리틀 엔디안에서 성능이 더 좋은 이유입니다. 두 바이트가 메모리에서 페치 될 때까지 기다려야하거나 역순으로 페치하면 시간이 더 오래 걸립니다.
이것은 오래된 8 비트 시스템에 있습니다. 현대의 CPU에서는 바이트 순서가 차이를 의심하고 역사적인 이유로 만 리틀 엔디안을 사용합니다.
8 비트 프로세서를 사용하면 확실히 더 효율적 이었으므로 다른 코드 나 추가 값을 버퍼링하지 않고도 8 비트 또는 16 비트 작업을 수행 할 수 있습니다.
한 번에 바이트를 처리하는 경우 일부 추가 작업에 더 좋습니다.
그러나 빅 엔디안이 더 자연스러운 이유는 없습니다. 영어에서는 13 (little endian)과 23 (big endian)을 사용합니다.
0x12345678
것처럼 저장됩니다 (바이트 0은 왼쪽에, 바이트 3은 오른쪽에 있음). 숫자가 클수록 (비트 단위) 스왑이 더 많이 필요하다는 점에 유의하십시오. WORD는 한 번의 스왑이 필요합니다. DWORD, 2 패스 (3 개의 총 스왑); QWORD 세 번 (총 7 번) 등. 즉 스왑입니다. 또 다른 옵션은 그들에게 모두 전달을 읽고 및 이전 버전 (각 바이트의 전달을 읽는하지만 뒤쪽으로 전체 #를 스캔). 78 56 34 12
12 34 56 78
(bits/8)-1
일본 날짜 규칙은 "big endian"-yyyy / mm / dd입니다. 이는 알고리즘을 정렬하는 데 편리하며 일반적인 첫 번째 문자는 가장 중요한 규칙과 간단한 문자열 비교를 사용할 수 있습니다.
가장 중요한 필드 우선 레코드에 저장된 빅 엔디안 숫자에도 비슷한 내용이 적용됩니다. 필드 내 바이트의 중요도는 레코드 내 필드의 중요성과 일치하므로 a memcmp
를 사용하여 레코드를 비교할 수 있지만 두 개의 Longword, 4 워드 또는 8 개의 개별 바이트를 비교하든 상관 없습니다.
필드의 중요성 순서를 바꾸면 동일한 이점을 얻을 수 있지만 빅 엔디안이 아닌 리틀 엔디안 번호에 해당합니다.
물론 이것은 실질적인 중요성이 거의 없습니다. 플랫폼이 빅 엔디안이든 리틀 엔디안이든 관계없이 필요한 경우이 트릭을 악용 할 레코드 필드를 주문할 수 있습니다. 이식 가능한 코드 를 작성해야하는 경우에는 고통 입니다.
고전적인 호소에 대한 링크를 포함시킬 수도 있습니다 ...
http://tools.ietf.org/rfcmarkup?url=ftp://ftp.rfc-editor.org/in-notes/ien/ien137.txt
편집하다
여분의 생각. 한 번 큰 정수 라이브러리를 작성했으며 (가능한 경우) 32 비트 너비 청크는 플랫폼이 해당 청크의 비트를 어떻게 정렬하는지에 관계없이 리틀 엔디안 순서로 저장됩니다. 그 이유는 ...
많은 알고리즘이 자연스럽게 최소한의 끝에서 작동하기 시작하고 그 끝이 일치되기를 원합니다. 예를 들어, 캐리는 점점 더 많은 유효 숫자로 전파되므로 최하위에서 시작하는 것이 좋습니다.
값을 늘리거나 줄이면 청크를 추가 / 제거한다는 의미이므로 청크를 위 / 아래로 이동할 필요가 없습니다. 메모리 재 할당으로 인해 여전히 복사가 필요할 수 있지만 자주는 아닙니다.
물론 이것은 프로세서와 밀접한 관련이 없습니다. CPU가 하드웨어 큰 정수 지원으로 만들어 질 때까지는 순수한 라이브러리입니다.
왜 이것이 이루어질 수 있는지에 대한 아무도 대답하지 않았습니다. 결과에 관한 많은 것들.
주어진 클럭 사이클에서 메모리에서 단일 바이트를로드 할 수있는 8 비트 프로세서를 고려하십시오.
이제 16 비트 값을로드하려는 16 비트 레지스터 (예 : 프로그램 카운터)에 16 비트 값을로드하려면 간단한 방법은 다음과 같습니다.
결과 : 페치 위치 만 늘리고 더 넓은 레지스터의 하위 부분에만로드하면 왼쪽으로 만 이동할 수 있습니다. (물론 오른쪽 이동은 다른 작업에 도움이되므로 약간의 부수적 인 쇼입니다.)
그 결과 16 비트 (더블 바이트) 항목이 대부분 순서대로 저장됩니다. 즉, 주소가 작을수록 가장 큰 바이트-큰 엔디안이 있습니다.
리틀 엔디안을 사용하여로드하려고하면 와이드 레지스터의 아래쪽에 바이트를로드 한 다음 스테이징 영역에 다음 바이트를로드하고 시프트 한 다음 더 넓은 레지스터의 상단에 팝해야합니다 . 또는 상위 또는 하위 바이트에 선택적으로로드 할 수 있도록보다 복잡한 게이팅 배열을 사용하십시오.
리틀 엔디안을 시도한 결과 더 많은 실리콘 (스위치 및 게이트)이 필요하거나 더 많은 작업이 필요합니다.
다시 말해, 예전에는 돈을 벌기 위해 대부분의 성능과 가장 작은 실리콘 영역에서 더 많은 돈을 벌었습니다.
요즘은 파이프 라인 채우기 등이 고려 사항과 거의 관련이없는,하지만 일이 있습니다 아직 큰 문제가 약간합니다.
S / W를 작성할 때 리틀 엔디 언 어드레싱을 사용하면 인생이 더 쉬워집니다.
(빅 엔디안 프로세서는 비트 -에 - 바이트의 관점에서 바이트 순서와 리틀 엔디안의 측면에서 큰 엔디안 경향이있다. 그러나 일부 프로세서 이상이며, 바이트 순서뿐만 아니라 주문 빅 엔디안 비트를 사용합니다.이 생활하게 매우를 메모리 매핑 주변 장치를 추가하는 하드웨어 디자이너에게는 흥미가 있지만 프로그래머에게는 다른 결과는 없습니다.)
Jimwise는 좋은 지적을했습니다. 리틀 엔디안에는 다음과 같은 문제가 있습니다.
byte data[4];
int num=0;
for(i=0;i<4;i++)
num += data[i]<<i*8;
OR
num = *(int*)&data; //is interpreted as
mov dword data, num ;or something similar it has been some time
메모리에서 스왑 된 위치의 명백한 단점에 영향을받지 않는 프로그래머에게는 더 직설적입니다. 나는 개인적으로 빅 엔디안이 자연스러워하는 것에 반한다고 생각합니다. :). 12는 21로 저장하고 작성해야합니다. :)
for(i=0; i<4; i++) { num += data[i] << (24 - i * 8); }
해당합니다 move.l data, num
.
나는 왜 누군가가 바이트를 역순으로 저장하고 싶어하는지 궁금합니다.
십진수는 빅 엔디안으로 작성됩니다. 또한 영어로 작성하는 방법 가장 중요한 숫자로 시작하고 다음으로 가장 중요한 숫자부터 가장 덜 중요한 숫자로 시작하십시오. 예 :
1234
천, 이백 삼십 사입니다.
이것은 빅 엔디안이 때때로 자연 질서라고 불리는 방식입니다.
리틀 엔디안에서이 숫자는 1, 2, 3, 3, 4 천입니다.
그러나 덧셈 또는 뺄셈과 같은 산술을 수행 할 때는 끝에서 시작합니다.
1234
+ 0567
====
4와 7로 시작하여 가장 낮은 숫자를 쓰고 캐리를 기억하십시오. 그런 다음 3과 6 등을 더합니다. 더하기, 빼기 또는 비교를 위해 이미 메모리를 순서대로 읽는 논리가 있고 숫자가 반대로되어 있으면 구현하는 것이 더 간단합니다.
이러한 방식으로 빅 엔디안을 지원하려면 메모리를 반대로 읽는 논리가 필요하거나 레지스터에서만 작동하는 RISC 프로세스가 있습니다. ;)
많은 Intel x86 / Amd x64 디자인은 역사적입니다.
가변 길이의 저장 및 전송 만 포함되지만 여러 값을 갖는 산술이없는 경우 LE는 일반적으로 작성하기 쉽고 BE는 읽기 쉽습니다.
구체적인 예로 int-to-string 변환을 수행해 봅시다.
int val_int = 841;
char val_str[] = "841";
int가 문자열로 변환되면 가장 작은 자릿수는 가장 중요한 자릿수보다 추출하기 쉽습니다. 간단한 종료 조건을 가진 간단한 루프에서 모두 수행 할 수 있습니다.
val_int = 841;
// Make sure that val_str is large enough.
i = 0;
do // Write at least one digit to care for val_int == 0
{
// Constants, can be optimized by compiler.
val_str[i] = '0' + val_int % 10;
val_int /= 10;
i++;
}
while (val_int != 0);
val_str[i] = '\0';
// val_str is now in LE "148"
// i is the length of the result without termination, can be used to reverse it
이제 BE 순서대로 시도하십시오. 일반적으로 특정 수 (여기서는 100)에 대해 최대 10의 거듭 제곱을 갖는 다른 제수가 필요합니다. 물론 이것을 먼저 찾아야합니다. 할 일이 훨씬 더 많습니다.
역 쓰기 조작으로 수행되면 BE에서 문자열을 정수로 변환하는 것이 더 쉽습니다. 쓰기는 가장 중요한 숫자를 마지막에 저장하므로 먼저 읽어야합니다.
val_int = 0;
length = strlen(val_str);
for (i = 0; i < length; i++)
{
// Again a simple constant that can be optimized.
val_int = 10*val_int + (val_str[i] - '0');
}
이제 LE 순서대로 동일하게 수행하십시오. 다시 한번, 1로 시작하고 각 자리수에 10을 곱한 추가 요소가 필요합니다.
따라서 값을 정확히 한 번 작성하지만 적어도 한 번 이상 여러 번 읽을 수 있으므로 저장에 BE를 사용하는 것이 좋습니다. 더 간단한 구조를 위해 일반적으로 경로를 이동하여 LE로 변환 한 다음 값을 두 번 쓰더라도 결과를 반대로 바꿉니다.
BE 스토리지의 다른 예로는 UTF-8 인코딩 등이 있습니다.