Apakah bit-shift tergantung pada endianness?

156

Misalkan nomor saya 'numb'=1025 [00000000 00000000 00000100 00000001]diwakili:

Pada Mesin Little-Endian:

00000001 00000100 00000000 00000000

Pada Mesin Big-Endian:

00000000 00000000 00000100 00000001

Sekarang, jika saya menerapkan Pergeseran Kiri pada 10 bit (yaitu: mati rasa << = 10), saya harus memiliki:

[A] Pada Mesin Little-Endian:

Seperti yang saya perhatikan di GDB, Little Endian melakukan Pergeseran Kiri dalam 3 langkah: [Saya telah menunjukkan Langkah '3' untuk lebih memahami pemrosesan saja]

  1. Obati no. dalam Konvensi Big-Endian:

    00000000        00000000        00000100    00000001
  2. Terapkan Kiri-Shift:

    00000000        00010000        00000100        00000000
  3. Mewakili Hasil lagi di Little-Endian:

    00000000        00000100        00010000        00000000 

[B]. Pada Mesin Big-Endian:

00000000        00010000        00000100        00000000

Pertanyaanku adalah:

Jika saya langsung menerapkan Pergeseran Kiri pada Konvensi Little Endian, itu harus memberikan:

numb:

00000001 00000100 00000000 00000000

numb << 10:

00010000 00000000 00000000 00000000

Tapi sebenarnya, itu memberi:

00000000        00000100        00010000        00000000 

Untuk mencapai hasil kedua saja, saya telah menunjukkan tiga langkah hipotetis di atas.

Tolong jelaskan kepada saya mengapa dua hasil di atas berbeda: Hasil aktual numb << 10berbeda dari hasil yang diharapkan.

Sandeep Singh
sumber

Jawaban:

194

Endianness adalah cara nilai disimpan dalam memori. Ketika dimuat ke dalam prosesor, terlepas dari endianness, instruksi bit shift beroperasi pada nilai dalam register prosesor. Oleh karena itu, memuat dari memori ke prosesor adalah setara dengan mengkonversi ke big endian, operasi pemindahan datang berikutnya dan kemudian nilai baru disimpan kembali dalam memori, yang merupakan tempat urutan byte endian kecil mulai berlaku lagi.

Perbarui, terima kasih kepada @jww: Pada PowerPC, vektor dan shiftnya berubah menjadi endian sensitif. Anda dapat memiliki nilai dalam register vektor dan perubahan akan menghasilkan hasil yang berbeda pada little-endian dan big-endian .

Carl
sumber
4
Terima kasih untuk penjelasannya. Bisakah Anda menyarankan beberapa referensi di mana saya bisa mendapatkan pemahaman yang lebih baik tentang seluk-beluk tersebut.
Sandeep Singh
4
Hal terbaik untuk memahami endianness adalah dengan benar-benar menggunakannya pada arsitektur yang berbeda pada tingkat tertanam. Namun, saya bisa merujuk Anda ke dua artikel ini: codeproject.com/KB/cpp/endianness.aspx dan ibm.com/developerworks/aix/library/au-endianc/...
Carl
3
Jadi kode saya akan berfungsi terlepas dari endian ?! ini bagus! Saya sudah sangat khawatir saya harus meretas kode saya ke neraka dan kembali!
MarcusJ
2
@ MarscJ: Tidak harus. Misalnya, jika Anda membaca 4 byte dari file yang mewakili integer 32-bit, Anda perlu mempertimbangkan endianness dari data yang Anda baca bersamaan dengan endianness dari sistem yang menerima data untuk menafsirkan dengan benar data.
Carl
3
Pada PowerPC, vektor bergeser dan berputar peka terhadap endian. Anda dapat memiliki nilai dalam register vektor dan perubahan akan menghasilkan hasil yang berbeda pada little-endian dan big-endian.
jww
58

Tidak, bitshift, seperti bagian C lainnya, didefinisikan dalam nilai , bukan representasi. Shift kiri oleh 1 adalah mutliplikasi oleh 2, shift kanan adalah pembagian. (Seperti biasa ketika menggunakan operasi bitwise, waspadalah terhadap penandatanganan. Semuanya terdefinisi dengan baik untuk tipe integral yang tidak ditandatangani.)

Kerrek SB
sumber
1
Ini pada dasarnya berlaku untuk bilangan bulat aritmatika, tetapi C memang menyediakan banyak kasus perilaku yang bergantung pada representasi.
Edmund
2
@ Edmund: Hm ... yang paling menonjol adalah implementasi dari signness tidak ditentukan, dan sebagai akibatnya perilaku operasi bitwise (seperti pergeseran kanan) dan modulo dan bagi adalah implementasi yang didefinisikan pada bilangan bulat negatif. Apa hal lain yang ada dalam benak Anda yang didefinisikan oleh implementasi?
Kerrek SB
@ KerrekSB sayangnya itu bukan implementasi yang didefinisikan pada bilangan bulat negatif. Mereka tidak ditentukan dalam C89 dan tidak terdefinisi dalam C99 +, yang merupakan ide yang sangat buruk.
Paolo Bonzini
@ PaoloBonzini: Ya, poin bagus. Sebenarnya itu bahkan lebih baik, karena memperkuat titik bahwa operasi shift didefinisikan dalam hal nilai, mungkin tidak terdefinisi ketika hasilnya tidak dapat diwakili, dan berspekulasi tentang representasi yang mendasari tidak membantu.
Kerrek SB
@ GerrekSB: masalahnya adalah semua orang benar-benar membutuhkan shift kiri untuk direpresentasikan sebagai nilai dan sebagai representasi, tergantung pada kasusnya. Dan menggunakan bilangan bulat yang tidak ditandatangani dapat menyebabkan masalah lain, misalnya x &= -1u << 20kemungkinan besar akan salah jika x64-bit dan int32-bit. Untuk alasan ini, GCC berjanji untuk tidak pernah memperlakukan shift yang ditandatangani sebagai tidak ditentukan atau bahkan tidak ditentukan.
Paolo Bonzini
5

Instruksi shift mana pun yang menggeser bit orde tinggi terlebih dahulu dianggap sebagai shift kiri. Instruksi shift mana pun yang menggeser bit orde rendah terlebih dahulu dianggap sebagai pergeseran yang tepat. Dalam hal itu, perilaku >>dan <<untuk unsignedangka tidak akan bergantung pada endianness.

Davislor
sumber
4

Komputer tidak menuliskan angka seperti yang kita lakukan. Nilainya hanya bergeser. Jika Anda bersikeras melihatnya byte-by-byte (meskipun itu bukan cara komputer melakukannya), Anda bisa mengatakan bahwa pada mesin little-endian, byte pertama bergeser ke kiri, kelebihan bit masuk ke byte kedua, dan seterusnya.

(Omong-omong, little-endian lebih masuk akal jika Anda menulis byte secara vertikal daripada horizontal, dengan alamat yang lebih tinggi di atas. Yang terjadi adalah bagaimana diagram peta memori umumnya digambar.)

Raymond Chen
sumber
2

Meskipun jawaban yang diterima menunjukkan bahwa endianess adalah konsep dari pandangan memori. Tapi saya tidak berpikir itu menjawab pertanyaan secara langsung.

Beberapa jawaban memberi tahu saya bahwa operasi bitwise tidak bergantung pada endianess , dan prosesor dapat mewakili byte dengan cara lain. Ngomong-ngomong, itu berbicara tentang endianess yang akan diabstraksikan.

Tetapi ketika kita melakukan perhitungan bitwise di kertas misalnya, tidak perlu menyatakan endianess di tempat pertama? Seringkali kita memilih endianess secara implisit.

Misalnya, anggap kita memiliki garis kode seperti ini

0x1F & 0xEF

Bagaimana Anda menghitung hasilnya dengan tangan, di atas kertas?

  MSB   0001 1111  LSB
        1110 1111
result: 0000 1111

Jadi di sini kita menggunakan format Big Endian untuk melakukan perhitungan. Anda juga dapat menggunakan Little Endian untuk menghitung dan mendapatkan hasil yang sama.

Btw, ketika kita menulis angka dalam kode, saya pikir itu seperti format Big Endian. 123456atau 0x1F, angka paling signifikan dimulai dari kiri.

Sekali lagi, segera setelah kita menulis beberapa format biner dari nilai di atas kertas, saya pikir kita sudah memilih Endianess dan kita melihat nilainya seperti yang kita lihat dari memori.

Jadi kembali ke pertanyaan, operasi shift <<harus dianggap bergeser dari LSB (byte paling signifikan) ke MSB (byte paling signifikan) .

Kemudian untuk contoh dalam pertanyaan:

numb=1025

Little Endian

LSB 00000001 00000100 00000000 00000000 MSB

Jadi << 10akan 10bitbergeser dari LSB ke MSB.


Perbandingan dan << 10operasi untuk format Little Endian langkah demi langkah:

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

Wow! Saya mendapatkan hasil yang diharapkan seperti yang dijelaskan OP!

Masalah yang OP tidak dapatkan hasil yang diharapkan adalah:

  1. Tampaknya dia tidak beralih dari LSB ke MSB.

  2. Ketika menggeser bit dalam format Little Endian, Anda harus menyadari (terima kasih Tuhan, saya sadari) bahwa:

LSB 10000000 00000000 MSB << 1adalah
LSB 00000000 00000001 MSB, tidak LSB 01000000 00000000 MSB

Karena untuk setiap individu 8bits, kami sebenarnya menulisnya dalam MSB 00000000 LSBformat Big Endian.

Jadi seperti itu

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


Untuk menyimpulkan:

  1. Meskipun operasi bitwise dikatakan diabstraksi dari blablablabla ..., ketika kita menghitung operasi bitwise dengan tangan, kita masih perlu mengetahui endianess apa yang kita gunakan saat kita menuliskan format biner di atas kertas. Kami juga perlu memastikan semua operator menggunakan endianess yang sama.

  2. OP tidak mendapatkan hasil yang diharapkan karena dia melakukan kesalahan shifting.

Rick
sumber