Чи залежить зміна бітів від витривалості?


156

Припустимо, у мене 'numb'=1025 [00000000 00000000 00000100 00000001]представлене число :

На машині Little Endian:

00000001 00000100 00000000 00000000

На машині Big-Endian:

00000000 00000000 00000100 00000001

Тепер, якщо я застосую Left Shift до 10 біт (тобто: numb << = 10), я повинен мати:

[A] На машині Little Endian:

Як я помітив у GDB, Little Endian виконує зсув ліворуч у 3 кроки: [Я показав "3" кроки, щоб краще зрозуміти обробку лише]

  1. Лікуйте ні. Конвенції Біг-Ендіан:

    00000000        00000000        00000100    00000001
  2. Застосувати ліворуч:

    00000000        00010000        00000100        00000000
  3. Представіть результат знову в Little-Endian:

    00000000        00000100        00010000        00000000 

[В]. На машині Big-Endian:

00000000        00010000        00000100        00000000

Моє запитання:

Якщо я безпосередньо застосую ліву зміну до Конвенції Малого Ендіану, вона повинна:

numb:

00000001 00000100 00000000 00000000

numb << 10:

00010000 00000000 00000000 00000000

Але насправді це дає:

00000000        00000100        00010000        00000000 

Щоб досягти лише другого результату, я показав три гіпотетичні кроки вище.

Будь ласка, поясніть мені, чому два вищезгадані результати відрізняються: Фактичний результат numb << 10відрізняється від очікуваного.

Відповіді:


194

Endianness - це спосіб збереження значень у пам'яті. При завантаженні в процесор, незалежно від витривалості, інструкція про зсув бітів працює на значення в регістрі процесора. Отже, завантаження з пам'яті на процесор є еквівалентом переходу до великого ендіана, наступна операція перемикання, а потім нове значення зберігається знову в пам'яті, саме там знову починає діяти маленький порядок байтів ендіан.

Оновлення, завдяки @jww: На PowerPC вектор зміщується та обертається, чутливий до ендіанців. Ви можете мати значення у векторному регістрі, і зсув дасть різні результати для маленьких та великих .


4
Дякую за пояснення. Чи можете ви підказати, будь ласка, якусь посилання, де я можу краще зрозуміти такі тонкощі.
Сандіп Сінгх

4
Найкраще для розуміння ендіанності - це реально використовувати його в різних архітектурах на вбудованому рівні. Однак я можу посилатись на ці дві статті: codeproject.com/KB/cpp/endianness.aspx та ibm.com/developerworks/aix/library/au-endianc/…
Карл

3
Тож мій код буде працювати незалежно від ендіана ?! це чудово! Я так хвилювався, що мені доведеться зламати код до пекла і назад!
MarcusJ

2
@MarcusJ: Не обов’язково. Наприклад, якщо ви читаєте 4 байти з файлу, що представляє собою 32-бітове ціле число, вам потрібно врахувати виправданість даних, які ви читаєте, в поєднанні з цінністю системи, що отримує дані, щоб правильно інтерпретувати дані.
Карл

3
На PowerPC вектор зміщується та обертається, чутливий до ендіан. Ви можете мати значення у векторному регістрі, і зсув дасть різні результати для маленьких та великих.
jww

58

Ні, бітшіфф, як і будь-яка інша частина С, визначається значеннями , а не уявленнями. Зсув вліво на 1 - це повторне зміщення на 2, праворух - це ділення. (Як завжди, використовуючи побітові операції, остерігайтеся підпису. Все найбільш чітко визначено для непідписаних інтегральних типів.)


1
Це в основному справедливо для цілочисельної арифметики, але C дійсно надає безліч випадків поведінки, залежної від представлення.
Едмунд

2
@Edmund: Гм ... найголовніше, що реалізація підпису не визначена, і як наслідок, поведінка побітових операцій (наприклад, зсув правої частини) та модуля та ділення визначаються реалізацією на від'ємні цілі числа. Які ще речі Ви пам’ятаєте, які визначаються реалізацією?
Керрек СБ

@KerrekSB, на жаль, вони не визначені реалізацією для від'ємних цілих чисел. Вони не визначені в C89 і не визначені в C99 +, що було дуже поганою ідеєю.
Паоло Бонзіні

@PaoloBonzini: Так, добре. Насправді це навіть краще, оскільки це підкреслює те, що операції зсуву визначаються за значеннями, можливо, не визначеними, коли результат не є репрезентабельним, і що спекуляція щодо базового представлення не допомагає.
Керрек СБ

@KerrekSB: річ у тому, що всі насправді потребують зміни лівої сторони, щоб їх представляти як цінності, так і як представлення, залежно від конкретного випадку. А використання непідписаних цілих чисел може спричинити інші проблеми, наприклад x &= -1u << 20, найімовірніше, буде неправильним, якщо xвін 64-бітний і int32-розрядний. З цієї причини GCC обіцяє ніколи не розцінювати підписані зміни як невизначені або навіть не визначені.
Паоло Бонзіні

5

Незалежно від того, яка інструкція зміни зміщується, біти вищого порядку спочатку вважаються зрушенням вліво. Незалежно від того, яка інструкція зміни змінює біти нижчого порядку спочатку, вважається правильним зсувом. У цьому сенсі поведінка >>і <<для unsignedчисел , не буде залежати від байтів.


4

Комп'ютери не записують числа так, як ми. Значення просто зміщується. Якщо ви наполягаєте на тому, щоб дивитись на нього по байтах (хоча це не так, як це робить комп'ютер), ви можете сказати, що на машині з невеликим ендіанством перший байт зміщується вліво, надлишки бітів переходять у другий байт, і так далі.

(До речі, little-endian має більше сенсу, якщо ви пишете байти вертикально, а не горизонтально, з більш високими адресами вгорі. Що трапляється як зазвичай малюються діаграми карти пам'яті.)


2

Хоча прийнята відповідь вказує, що ендіатизм - це поняття з точки зору пам'яті. Але я не думаю, що це відповідає безпосередньо на питання.

Деякі відповіді говорять мені про те, що побітові операції не залежать від витривалості , і процесор може представляти байти будь-яким іншим способом. У всякому разі, це говорить про те, що ендіазність стає абстрагованою.

Але, наприклад, коли ми робимо кілька розрядних підрахунків на папері, чи не потрібно в першу чергу констатувати цілеспрямованість? У більшості випадків ми вибираємо ендіатичність неявно.

Наприклад, припустимо, у нас є такий рядок коду

0x1F & 0xEF

Як би ви обчислили результат вручну, на папері?

  MSB   0001 1111  LSB
        1110 1111
result: 0000 1111

Отже, тут ми використовуємо формат Big Endian для розрахунку. Ви також можете використовувати Little Endian для обчислення та отримання однакового результату.

До речі, коли ми пишемо числа в коді, я думаю, що це як формат Big Endian. 123456або 0x1F, найбільш значні числа починаються зліва.

Знову ж таки, як тільки ми записуємо на папері якийсь двійковий формат значення, я думаю, що ми вже вибрали Endianess, і ми переглядаємо значення так, як ми його бачимо з пам'яті.

Тож назад до питання, операцію зсуву <<слід розглядати як перехід від LSB (найменш значущий байт) до MSB (найбільш значущий байт) .

Тоді як для прикладу у питанні:

numb=1025

Маленький Ендіан

LSB 00000001 00000100 00000000 00000000 MSB

Так << 10було б 10bitперехід від LSB до MSB.


Порівняння та << 10операції для формату Little 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!)

Оце Так! Я отримую очікуваний результат, як описано в ОП!

Проблеми, через які ОП не отримала очікуваного результату, полягають у тому, що:

  1. Схоже, що він не перейшов від LSB до MSB.

  2. Під час переміщення бітів у форматі Little Endian ви повинні усвідомити (слава богу, я це розумію), що:

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. Хоча побитові операції, як кажуть, абстрагуються від блаблаблабла ..., коли ми обчислюємо побітові операції вручну, нам все одно потрібно знати, яку цілеспрямованість ми використовуємо, записуючи на папері двійковий формат. Також нам потрібно переконатися, що всі оператори використовують однакову ендіативність.

  2. ОП не отримав очікуваного результату через те, що він змінився неправильно.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.