Процесори Intel (і, можливо, деякі інші) використовують для зберігання маленький формат ендіан.
Мені завжди цікаво, чому хтось хотів би зберігати байти у зворотному порядку. Чи має цей формат якісь переваги перед великим ендіанським форматом?
Процесори Intel (і, можливо, деякі інші) використовують для зберігання маленький формат ендіан.
Мені завжди цікаво, чому хтось хотів би зберігати байти у зворотному порядку. Чи має цей формат якісь переваги перед великим ендіанським форматом?
Відповіді:
Існують аргументи в будь-якому випадку, але один момент полягає в тому, що в системі мало ендіанів адреса заданого значення в пам'яті, прийнята як 32, 16 або 8 бітова ширина, однакова.
Іншими словами, якщо у вас є в пам'яті значення двох байтів:
0x00f0 16
0x00f1 0
прийняття цього "16" як 16-бітного значення (c "коротке" в більшості 32-бітних систем) або як 8-бітове значення (як правило, c 'char') змінює лише інструкцію, яку ви використовуєте, а не адресу, яку ви отримуєте з.
У системі з великим ендіаном, із зазначеним вище:
0x00f0 0
0x00f1 16
вам потрібно збільшити покажчик, а потім виконати більш вузьку операцію з отримання нового значення.
Отже, коротше кажучи, "у маленьких ендіанських системах касти є неоперативними".
Мені завжди цікаво, чому хтось хотів би зберігати байти у зворотному порядку.
Біг-ендіан і малий-ендіан - це лише "нормальний порядок" і "зворотний порядок" з точки зору людини, і тільки тоді, якщо все це правдиво ...
Це все людські конвенції, які взагалі не мають значення для процесора. Якби ви зберегли №1 та №2 та перевернули №3, маленький ендіанець здавався б "цілком природним" людям, які читають арабською чи івритом, які написані справа наліво.
І є й інші людські умовності, які роблять великого ендіана, який здається неприродним, як ...
Ще коли я програмував 68K та PowerPC, я вважав, що big-endian є "правильним", а little-endian - "неправильним". Але оскільки я займаюся більшою роботою ARM та Intel, я звик до маленьких ендіян. Це насправді не має значення.
Гаразд, ось чому я пояснив мені причину: Додавання і віднімання
Коли ви додаєте або віднімаєте багатобайтові числа, вам слід почати з найменш значущого байта. Якщо ви додаєте, наприклад, два 16-бітні числа, може бути перенесення від найменш значущого байта до найзначнішого байта, тому вам доведеться почати з найменш значущого байта, щоб побачити, чи є перенос. Це та сама причина, що ви починаєте з правої цифри під час складання з великої руки. Не можна починати зліва.
Розглянемо 8-бітну систему, яка послідовно отримує байти з пам'яті. Якщо він спочатку отримує найменш значущий байт , він може почати робити додавання, тоді як найзначніший байт видобувається з пам'яті. Цей паралелізм є причиною того, що продуктивність краща у маленьких ендіян на таких, як система. Якби довелося чекати, поки обидва байти витягнуті з пам'яті або отримати їх у зворотному порядку, це займе більше часу.
Це в старих 8-бітних системах. У сучасному процесорі я сумніваюся, що порядок байтів має будь-яке значення, і ми використовуємо мало ендіанів лише з історичних причин.
З 8-бітовими процесорами це було, безумовно, більш ефективно, ви можете виконати 8 або 16-бітну операцію, не потребуючи іншого коду та не потребуючи буферування додаткових значень.
Ще краще для деяких операцій додавання, якщо ви одночасно займаєтеся байтом.
Але немає причин, що біг-ендіан є більш природним - в англійській мові ви використовуєте тринадцять (маленький ендіан) і двадцять три (великий ендіан)
0x12345678
зберігається як у 78 56 34 12
той час, як у системі BE це 12 34 56 78
(байт 0 - зліва, байт 3 - справа). Зверніть увагу, чим більша кількість (у бітах), тим більше потрібно їх заміни; WORD вимагатиме однієї заміни; DWORD, два проходи (три загальних свопи); QWORD три проходи (7 усього) тощо. Тобто, (bits/8)-1
свопи. Інший варіант - читання їх як вперед, так і назад (читання кожного байта вперед, але сканування всього # назад).
Японська конвенція про дату є "великою ендіанською" - yyyy / мм / дд. Це зручно для алгоритмів сортування, в яких можна використовувати просте рядкове порівняння зі звичайним правилом першого символу - це найзначніше.
Щось подібне стосується і номерів великих ендіанів, які зберігаються в найбільш значущому першому полі запису. Порядок значущості байтів у полях відповідає значенню полів у записі, тому ви можете використовувати a memcmp
для порівняння записів, не піклуючись, чи порівнюєте ви два довгих слова, чотири слова чи вісім окремих байтів.
Переверніть порядок значущості полів, і ви отримаєте ту ж перевагу, але для малих-ендіанських чисел, а не для великих-ендіанів.
Це, звичайно, має дуже мало практичного значення. Незалежно від того, чи є ваша платформа великим або маленьким, ви можете замовити поля записів, щоб використовувати цей трюк, якщо вам це справді потрібно. Це просто біль, якщо вам потрібно написати портативний код.
Я також можу включити посилання на класичне звернення ...
http://tools.ietf.org/rfcmarkup?url=ftp://ftp.rfc-editor.org/in-notes/ien/ien137.txt
EDIT
Зайва думка. Я колись написав велику цілочисельну бібліотеку (щоб побачити, чи зможу я), і для цього 32-бітні шматки зберігаються в малопомітному порядку, незалежно від того, як платформа впорядковує біти в цих фрагментах. Причини були ...
Дуже багато алгоритмів, природно, починають працювати в найменш значущому кінці, і хочуть, щоб ці цілі були узгоджені. Наприклад, крім того, носій пропонує все більш і більш значні цифри, тому є сенс починати з найменш значущого кінця.
Зростання або зменшення значення означає лише додавання / видалення шматочків наприкінці - не потрібно переміщувати шматки вгору / вниз. Копіювання все ж може знадобитися через перерозподіл пам’яті, але не часто.
Це, очевидно, не стосується процесорів, звичайно - поки процесори не створені з апаратною підтримкою великих цілих чисел, це суто бібліотечна річ.
Ніхто ще не відповів ЧОМУ це можна зробити, багато чого про наслідки.
Розглянемо 8-бітний процесор, який може завантажувати один байт з пам'яті за заданий тактовий цикл.
Тепер, якщо ви хочете завантажити 16-бітове значення, скажіть (скажімо) один і єдиний у вас 16-бітний реєстр - тобто лічильник програми, то простий спосіб зробити це:
результат: ви тільки коли-небудь збільшуєте місце отримання даних, ви тільки коли-небудь завантажуєтесь у низький порядок частини вашої ширшої реєстрації, і вам потрібно лише мати змогу зрушити ліворуч. (Звичайно, зсув праворуч корисний для інших операцій, тому ця частина є побічним шоу.)
Наслідком цього є те, що 16-бітний (двобайтовий) матеріал зберігається в порядку Most..Least. Тобто менша адреса має найзначніший байт - такий великий ендіан.
Якщо ви замість цього намагалися завантажити мало ендіан, вам потрібно буде завантажити байт у нижню частину вашого широкого регістра, а потім завантажити наступний байт у область постановки, змістити його, а потім вивести його у верхню частину ширшого регістру . Або скористайтеся більш складним розташуванням решетування, щоб мати можливість вибірково завантажуватися у верхній або нижній байт.
Результатом спроби пройти трохи ендіан - вам або потрібно більше кремнію (вимикачі і ворота), або більше операцій.
Іншими словами, з точки зору повернення грошей за старі часи, ви отримали більше ударів для більшої продуктивності та найменшої площі кремнію.
У наші дні ці міркування і вкрай неактуальні, але такі речі, як заливка трубопроводів, все ж можуть бути великою справою.
Що стосується написання с / ш, життя часто простіше, коли використовується мало ендіанських адрес.
(І великі ендіанські процесори, як правило, є великими ендіанами з точки зору впорядкування байтів і мало ендіанів з точки зору біт-байтів. Але деякі процесори дивні, і вони будуть використовувати великі впорядкування біт-ендіанів, а також впорядкування байтів. Це робить життя дуже цікаво для дизайнера, що додає ч / б додавання периферійних пристроїв, орієнтованих на пам'ять, але це не має іншого наслідку для програміста.)
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
є тисяча, двісті тридцять чотири.
Таким чином, великий ендіан іноді називають природним порядком.
У маленькому ендіані це число склало б одну, двадцять, триста чотири тисячі.
Однак, виконуючи такі арифметичні, як додавання чи віднімання, ви починаєте з кінця.
1234
+ 0567
====
Ви починаєте з 4 і 7, пишете найнижчу цифру і запам'ятовуєте перенесення. Потім ви додаєте 3 і 6 і т. Д. Для додавання, віднімання або порівняння їх простіше реалізувати, якщо ви вже маєте логіку для читання пам'яті в порядку, якщо числа перевернуті.
Щоб підтримати великий ендіан таким чином, вам потрібна логіка для читання пам'яті в зворотному порядку, або у вас є RISC-процес, який працює тільки на регістри. ;)
Багато дизайну Intel x86 / Amd x64 є історичним.
Біг-ендіан є корисним для деяких операцій (порівняння "bignums" однакової довжини октету довжиною до душі). Літ-ендіан для інших (додавання, можливо, двох "бінгумів"). Зрештою, це залежить від того, для чого було встановлено апаратне забезпечення процесора, як правило, це те чи інше (деякі мікросхеми MIPS були, IIRC, з перемиканням на завантаження, щоб бути LE або BE).
Якщо задіяні лише зберігання та передача зі змінною довжиною, але немає арифметики з кількома значеннями, тоді LE зазвичай простіше писати, тоді як BE легше читати.
Візьмемо конкретний приклад int-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. Зазвичай вам потрібен інший дільник, який має найбільшу потужність 10 для конкретного числа (тут 100). Спершу потрібно це знайти, звичайно. Ще багато чого робити.
Перетворення рядка в int простіше зробити в 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 та багато іншого.