Liệu bit-shift phụ thuộc vào endianness?


155

Giả sử tôi có số 'numb'=1025 [00000000 00000000 00000100 00000001]đại diện:

Trên máy Little-Endian:

00000001 00000100 00000000 00000000

Trên máy Big-Endian:

00000000 00000000 00000100 00000001

Bây giờ, nếu tôi áp dụng Shift trái trên 10 bit (ví dụ: tê << = 10), tôi sẽ có:

[A] Trên máy Little-Endian:

Như tôi đã nhận thấy trong GDB, Little Endian thực hiện Shift trái theo 3 bước: [Tôi đã chỉ ra '3' Các bước để hiểu rõ hơn về xử lý]

  1. Đối xử không. trong Công ước Big-Endian:

    00000000        00000000        00000100    00000001
  2. Áp dụng phím trái:

    00000000        00010000        00000100        00000000
  3. Đại diện cho kết quả một lần nữa trong Little-Endian:

    00000000        00000100        00010000        00000000 

[B]. Trên máy Big-Endian:

00000000        00010000        00000100        00000000

Câu hỏi của tôi là:

Nếu tôi trực tiếp áp dụng Shift trái cho Công ước Little Endian, nó sẽ cung cấp:

numb:

00000001 00000100 00000000 00000000

numb << 10:

00010000 00000000 00000000 00000000

Nhưng thực ra, nó mang lại:

00000000        00000100        00010000        00000000 

Để chỉ đạt được kết quả thứ hai, tôi đã chỉ ra ba bước giả thuyết ở trên.

Vui lòng giải thích cho tôi tại sao hai kết quả trên lại khác nhau: Kết quả thực tế numb << 10khác với kết quả mong đợi.

Câu trả lời:


194

Endianness là cách các giá trị được lưu trữ trong bộ nhớ. Khi được tải vào bộ xử lý, bất kể tuổi thọ, lệnh dịch chuyển bit đang hoạt động dựa trên giá trị trong thanh ghi của bộ xử lý. Do đó, tải từ bộ nhớ sang bộ xử lý tương đương với chuyển đổi sang endian lớn, hoạt động dịch chuyển tiếp theo và sau đó giá trị mới được lưu lại trong bộ nhớ, đó là nơi thứ tự byte cuối nhỏ có hiệu lực trở lại.

Cập nhật, nhờ @jww: Trên PowerPC, vectơ dịch chuyển và xoay là nhạy cảm về cuối. Bạn có thể có một giá trị trong một thanh ghi vector và một sự thay đổi sẽ tạo ra các kết quả khác nhau trên endian nhỏ và endian lớn .


4
Cảm ơn đã giải thích. Bạn có thể vui lòng đề nghị một số tài liệu tham khảo nơi tôi có thể hiểu rõ hơn về những rắc rối như vậy.
Sandeep Singh

4
Điều tốt nhất để hiểu về endian là thực sự sử dụng nó trên các kiến ​​trúc khác nhau ở cấp độ nhúng. Tuy nhiên, tôi có thể giới thiệu bạn đến hai bài báo này: codeproject.com/KB/cpp/endianness.aspxibm.com/developerworks/aix/library/au-endianc/...
Carl

3
Vì vậy, mã của tôi sẽ hoạt động bất kể endian?! điều đó thật tuyệt! Tôi đã lo lắng rằng tôi phải hack mã của tôi xuống địa ngục và quay trở lại!
MarcusJ

2
@MarcusJ: Không nhất thiết. Ví dụ: nếu bạn đang đọc 4 byte từ một tệp đại diện cho số nguyên 32 bit, bạn cần xem xét độ bền của dữ liệu bạn đang đọc kết hợp với độ bền của hệ thống nhận dữ liệu để diễn giải đúng dữ liệu.
Carl

3
Trên PowerPC, vectơ dịch chuyển và xoay là nhạy cảm về cuối. Bạn có thể có một giá trị trong một thanh ghi vector và một sự thay đổi sẽ tạo ra các kết quả khác nhau trên endian nhỏ và endian lớn.
jww

58

Không, bitshift, giống như bất kỳ phần nào khác của C, được xác định theo giá trị , không phải biểu diễn. Dịch chuyển trái 1 bằng cách thay đổi bằng 2, dịch chuyển phải là phân chia. (Như mọi khi khi sử dụng các thao tác bitwise, hãy cẩn thận với chữ ký. Mọi thứ được xác định rõ nhất cho các loại tích phân không dấu.)


1
Điều này về cơ bản đúng với số học số nguyên, nhưng C cung cấp nhiều trường hợp hành vi phụ thuộc vào đại diện.
Edmund

2
@Edmund: Hừm ... đáng chú ý nhất là việc thực hiện ký kết không được chỉ định và do đó, hành vi của các hoạt động bitwise (như dịch chuyển phải) và modulo và phân chia được thực hiện được xác định trên các số nguyên âm. Những điều khác bạn có trong tâm trí được xác định thực hiện?
Kerrek SB

@KerrekSB rất tiếc, họ không triển khai được xác định trên các số nguyên âm. Chúng không được chỉ định trong C89 và không được xác định trong C99 +, đó là một ý tưởng rất tồi.
Paolo Bonzini

@PaoloBonzini: Vâng, điểm tốt. Trên thực tế điều đó thậm chí còn tốt hơn, vì nó củng cố quan điểm rằng các hoạt động thay đổi được xác định theo các giá trị, có thể không được xác định khi kết quả không thể biểu thị được và việc suy đoán về biểu diễn cơ bản không giúp ích gì.
Kerrek SB

@KerrekSB: điều thực sự là tất cả mọi người thực sự cần dịch chuyển trái để được biểu diễn cả dưới dạng giá trị và dưới dạng đại diện, tùy thuộc vào trường hợp. Và việc sử dụng các số nguyên không dấu có thể gây ra các vấn đề khác, ví dụ như x &= -1u << 20rất có thể sẽ không chính xác nếu xlà 64 bit và int32 bit. Vì lý do này, GCC hứa sẽ không bao giờ coi các ca làm việc đã ký là không xác định hoặc thậm chí không xác định.
Paolo Bonzini

5

Bất kỳ lệnh dịch chuyển nào sẽ dịch chuyển các bit bậc cao hơn trước tiên được coi là dịch chuyển trái. Bất kỳ lệnh dịch chuyển nào làm dịch chuyển các bit bậc thấp hơn trước đều được coi là dịch chuyển đúng. Theo nghĩa đó, hành vi của >><<cho các unsignedcon số sẽ không phụ thuộc vào endianness.


4

Máy tính không ghi số theo cách chúng ta làm. Giá trị chỉ đơn giản là thay đổi. Nếu bạn khăng khăng nhìn vào từng byte một (mặc dù đó không phải là cách máy tính làm việc đó), bạn có thể nói rằng trên một máy cuối nhỏ, byte đầu tiên dịch chuyển sang trái, các bit thừa sẽ chuyển sang byte thứ hai, và như thế.

(Nhân tiện, little endian có ý nghĩa hơn nếu bạn viết các byte theo chiều dọc thay vì theo chiều ngang, với địa chỉ cao hơn ở trên. Điều này xảy ra là cách sơ đồ bản đồ bộ nhớ thường được vẽ.)


1

Mặc dù câu trả lời được chấp nhận chỉ ra rằng endianess là một khái niệm từ chế độ xem bộ nhớ. Nhưng tôi không nghĩ rằng trả lời câu hỏi trực tiếp.

Một số câu trả lời cho tôi biết rằng các phép toán bitwise không phụ thuộc vào endianess và bộ xử lý có thể biểu diễn các byte theo bất kỳ cách nào khác. Dù sao, nó đang nói về endianess được trừu tượng hóa.

Nhưng khi chúng ta thực hiện một số tính toán từng chút một trên tờ giấy chẳng hạn, không cần nêu trạng thái cuối cùng ở vị trí đầu tiên? Hầu hết thời gian chúng tôi chọn một endianess ngầm.

Ví dụ: giả sử chúng ta có một dòng mã như thế này

0x1F & 0xEF

Làm thế nào bạn sẽ tính toán kết quả bằng tay, trên một tờ giấy?

  MSB   0001 1111  LSB
        1110 1111
result: 0000 1111

Vì vậy, ở đây chúng tôi sử dụng định dạng Big Endian để thực hiện tính toán. Bạn cũng có thể sử dụng Little Endian để tính toán và nhận được kết quả tương tự.

Btw, khi chúng tôi viết số bằng mã, tôi nghĩ nó giống như một định dạng Big Endian. 123456hoặc 0x1F, hầu hết các số có ý nghĩa bắt đầu từ bên trái.

Một lần nữa, ngay khi chúng tôi viết một số định dạng nhị phân của một giá trị lên tờ giấy, tôi nghĩ rằng chúng tôi đã chọn một Endianess và chúng tôi đang xem giá trị như chúng tôi thấy từ bộ nhớ.

Vì vậy, quay trở lại câu hỏi, một hoạt động thay đổi <<nên được coi là chuyển từ LSB (byte có ý nghĩa nhỏ nhất) sang MSB (byte quan trọng nhất) .

Sau đó, như ví dụ trong câu hỏi:

numb=1025

Little Endian

LSB 00000001 00000100 00000000 00000000 MSB

Vì vậy, << 10sẽ được 10bitchuyển từ LSB sang MSB.


So sánh và << 10thao tác cho định dạng Little Endian từng bước:

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

Ồ Tôi nhận được kết quả mong đợi như OP mô tả!

Các vấn đề mà OP không nhận được kết quả như mong đợi là:

  1. Có vẻ như anh ấy đã không chuyển từ LSB sang MSB.

  2. Khi dịch chuyển bit theo định dạng Little Endian, bạn nên nhận ra (cảm ơn chúa tôi nhận ra điều đó) rằng:

LSB 10000000 00000000 MSB << 1
LSB 00000000 00000001 MSB, không LSB 01000000 00000000 MSB

Bởi vì đối với mỗi cá nhân 8bits, chúng tôi thực sự đang viết nó theo MSB 00000000 LSBđịnh dạng Big Endian.

Vì vậy, nó giống như

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


Tóm lại:

  1. Mặc dù các thao tác bitwise được cho là đã được trừu tượng hóa bằng blablablabla ..., khi chúng ta tính toán các thao tác bitwise bằng tay, chúng ta vẫn cần biết chúng ta đang sử dụng cái gì khi chúng ta viết ra định dạng nhị phân trên giấy. Ngoài ra, chúng ta cần đảm bảo tất cả các toán tử sử dụng cùng một endianess.

  2. OP đã không nhận được kết quả như mong đợi là vì anh ta đã làm sai.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.