การเปลี่ยนบิตขึ้นอยู่กับ endianness หรือไม่?


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 ทำ Shift ซ้ายใน 3 ขั้นตอน: [ฉันได้แสดงขั้นตอน '3' เพื่อให้เข้าใจการประมวลผลดีขึ้นเท่านั้น]

  1. ปฏิบัติต่อหมายเลข ในอนุสัญญา Big-Endian:

    00000000        00000000        00000100    00000001
  2. ใช้ Shift ซ้าย:

    00000000        00010000        00000100        00000000
  3. แสดงผลลัพธ์อีกครั้งใน Little-Endian:

    00000000        00000100        00010000        00000000 

[b] บนเครื่อง Big-Endian:

00000000        00010000        00000100        00000000

คำถามของฉันคือ:

หากฉันใช้ Shift ซ้ายโดยตรงกับอนุสัญญา Little Endian ก็ควรให้:

numb:

00000001 00000100 00000000 00000000

numb << 10:

00010000 00000000 00000000 00000000

แต่จริงๆแล้วมันให้:

00000000        00000100        00010000        00000000 

เพื่อให้ได้ผลลัพธ์ที่สองเท่านั้นฉันได้แสดงสามขั้นตอนตามสมมติฐานด้านบน

โปรดอธิบายว่าเพราะเหตุใดผลลัพธ์สองข้อข้างต้นจึงแตกต่าง: ผลลัพธ์ที่แท้จริงของnumb << 10แตกต่างจากผลลัพธ์ที่คาดไว้

คำตอบ:


194

Endianness เป็นวิธีที่เก็บค่าไว้ในหน่วยความจำ เมื่อโหลดเข้าไปในหน่วยประมวลผลโดยไม่คำนึงถึง endianness คำสั่งการเลื่อนบิตจะทำงานกับค่าในการลงทะเบียนของโปรเซสเซอร์ ดังนั้นการโหลดจากหน่วยความจำไปยังหน่วยประมวลผลเทียบเท่ากับการแปลงไปสู่ ​​endian ขนาดใหญ่การดำเนินการเปลี่ยนมาถัดจากนั้นค่าใหม่จะถูกเก็บไว้ในหน่วยความจำ

อัปเดตด้วย @jww: บน PowerPC เวคเตอร์การหมุนและการหมุนจะอ่อนไหว endian คุณสามารถมีค่าได้ในการลงทะเบียนแบบเวกเตอร์และการเปลี่ยนแปลงจะให้ผลลัพธ์ที่แตกต่างกันสำหรับผู้ที่มีรายย่อยและผู้มีรายได้น้อย


4
ขอบคุณสำหรับคำอธิบาย คุณช่วยแนะนำการอ้างอิงบางส่วนที่ฉันสามารถเข้าใจความซับซ้อนได้ดีขึ้น
Sandeep Singh เมื่อ

4
สิ่งที่ดีที่สุดสำหรับการทำความเข้าใจกับ endianness คือการใช้มันกับสถาปัตยกรรมที่แตกต่างกันในระดับฝังตัว แต่ผมจะดูให้คุณทั้งสองบทความ: codeproject.com/KB/cpp/endianness.aspxและibm.com/developerworks/aix/library/au-endianc/...
คาร์ล

3
ดังนั้นรหัสของฉันจะทำงานโดยไม่คำนึงถึง endian?! มันยอดเยี่ยมมาก! ฉันกังวลมากฉันจะต้องแฮ็ครหัสของฉันไปที่นรกและย้อนกลับ!
MarcusJ

2
@MarcusJ: ไม่จำเป็น ตัวอย่างเช่นหากคุณกำลังอ่าน 4 ไบต์จากไฟล์ที่เป็นจำนวนเต็ม 32 บิตคุณต้องพิจารณาความเป็นหนึ่งของข้อมูลที่คุณกำลังอ่านควบคู่กับ endianness ของระบบที่รับข้อมูลเพื่อตีความอย่างถูกต้อง ข้อมูล.
Carl

3
บน PowerPC เวคเตอร์การหมุนและการหมุนจะอ่อนไหว endian คุณสามารถมีค่าในการลงทะเบียนแบบเวกเตอร์และการเปลี่ยนแปลงจะให้ผลลัพธ์ที่แตกต่างกันสำหรับผู้ที่มีรายย่อยและผู้มีรายได้น้อย
jww

58

ไม่บิตชิพเช่นเดียวกับส่วนอื่น ๆ ของ C ถูกกำหนดในรูปของค่าไม่ใช่การแทนค่า Left-shift by 1 เป็นการ mutliplication โดย 2 การเลื่อนด้านขวาเป็นการหาร (เช่นเคยทุกครั้งเมื่อใช้การดำเนินการระดับบิตระวังการเซ็นชื่อทุกอย่างถูกกำหนดอย่างดีที่สุดสำหรับประเภทอินทิกรัลที่ไม่ได้ลงชื่อ)


1
นี่เป็นความจริงสำหรับเลขคณิตเลขจำนวนเต็ม แต่ C มีหลายกรณีของพฤติกรรมที่ขึ้นกับการเป็นตัวแทน
Edmund

2
@Edmund: หืมมมม ... ส่วนใหญ่ที่ไม่ได้ระบุการใช้งานของ signness และผลที่ตามมาคือพฤติกรรมของการดำเนินการระดับบิต (เช่นการเลื่อนขวา) และโมดูโลและการหารถูกกำหนดบนจำนวนเต็มลบ มีสิ่งอื่นใดอีกบ้างที่คุณนึกถึงซึ่งกำหนดโดยการนำไปปฏิบัติ
Kerrek SB

@KerrekSB โชคไม่ดีที่พวกมันไม่ได้ถูกกำหนดบนจำนวนเต็มลบ พวกเขาไม่ได้ระบุใน C89 และไม่ได้กำหนดใน C99 + ซึ่งเป็นความคิดที่แย่มาก
เปาโล Bonzini

@PaoloBonzini: ใช่จุดดี ที่จริงแล้วยังดีกว่าเนื่องจากเป็นการตอกย้ำจุดที่การดำเนินการกะถูกกำหนดในแง่ของค่าอาจไม่ได้กำหนดเมื่อผลลัพธ์ไม่สามารถแทนได้และการคาดเดาเกี่ยวกับการเป็นตัวแทนต้นแบบไม่ได้ช่วยอะไร
Kerrek SB

@ KerrekSB: สิ่งที่ทุกคนต้องการให้มีการเลื่อนด้านซ้ายเพื่อแสดงถึงค่าและการเป็นตัวแทนขึ้นอยู่กับกรณี และการใช้จำนวนเต็มที่ไม่ได้ลงชื่ออาจทำให้เกิดปัญหาอื่น ๆ ตัวอย่างเช่นx &= -1u << 20ส่วนใหญ่จะไม่ถูกต้องหากxเป็น 64- บิตและint32- บิต ด้วยเหตุผลนี้ GCC สัญญาว่าจะไม่ปฏิบัติต่อการเปลี่ยนแปลงที่ลงนามเป็นไม่ได้กำหนดหรือไม่ได้ระบุไว้
เปาโลบอนซินี

5

คำสั่ง shift ใดที่เลื่อนบิตลำดับสูงกว่าก่อนจะถือว่าเป็น shift ซ้าย ไม่ว่าคำสั่ง shift ใดที่เลื่อนบิตที่มีลำดับต่ำกว่าออกไปก่อนจะถือว่าเป็นการเปลี่ยนที่ถูกต้อง ในแง่นั้นพฤติกรรมของ>>และ<<สำหรับunsignedตัวเลขจะไม่ขึ้นอยู่กับ endianness


4

คอมพิวเตอร์ไม่เขียนตัวเลขตามที่เราทำ ค่าเพียงแค่เลื่อน หากคุณยืนยันที่จะมองมันทีละไบต์ (แม้ว่านั่นจะไม่ใช่วิธีที่คอมพิวเตอร์ทำ) คุณก็สามารถพูดได้ว่าบนเครื่องเล็ก ๆ น้อย ๆ ที่เปลี่ยนไปไบต์แรกจะเหลือบิตที่เหลือเข้าไปในไบต์ที่สอง และอื่น ๆ

(โดยวิธีการ, little-endian เหมาะสมกว่าถ้าคุณเขียนไบต์ในแนวตั้งแทนที่จะเป็นแนวนอน, โดยมีที่อยู่สูงกว่าอยู่ด้านบนซึ่งเกิดขึ้นกับวิธีวาดแผนผังหน่วยความจำทั่วไป)


2

แม้ว่าคำตอบที่ได้รับการยอมรับชี้ให้เห็นว่า endianess เป็นแนวคิดจากมุมมองหน่วยความจำ แต่ฉันไม่คิดว่าจะตอบคำถามโดยตรง

คำตอบบางคำบอกฉันว่าการทำงานระดับบิตไม่ได้ขึ้นอยู่กับ endianessและโปรเซสเซอร์อาจเป็นตัวแทนของไบต์ด้วยวิธีอื่น อย่างไรก็ตามมันกำลังพูดถึง endianess นั้นถูกทำให้เป็นนามธรรม

แต่เมื่อเราทำการคำนวณระดับบิตบนกระดาษไม่จำเป็นต้องระบุจุดสิ้นสุดในตอนแรก? เวลาส่วนใหญ่เราเลือก endianess โดยปริยาย

ตัวอย่างเช่นสมมติว่าเรามีบรรทัดของรหัสเช่นนี้

0x1F & 0xEF

คุณจะคำนวณผลลัพธ์ด้วยมือลงบนกระดาษได้อย่างไร

  MSB   0001 1111  LSB
        1110 1111
result: 0000 1111

ดังนั้นที่นี่เราใช้รูปแบบ Big Endian ทำการคำนวณ คุณสามารถใช้ Little Endian เพื่อคำนวณและรับผลลัพธ์เดียวกันได้

Btw เมื่อเราเขียนตัวเลขในรหัสฉันคิดว่ามันเป็นรูปแบบ Big Endian 123456หรือ0x1Fตัวเลขที่สำคัญที่สุดเริ่มจากด้านซ้าย

อีกครั้งทันทีที่เราเขียนรูปแบบไบนารีของค่าลงบนกระดาษฉันคิดว่าเราได้เลือก Endianess แล้วและเรากำลังดูค่าตามที่เราเห็นจากหน่วยความจำ

ดังนั้นกลับไปที่คำถามที่การดำเนินการเปลี่ยนแปลง<<ควรจะคิดเป็นขยับจาก LSB (ไบต์ที่สำคัญน้อยที่สุด) เพื่อ MSB (ส่วนใหญ่ไบต์ที่สำคัญ)

จากนั้นเป็นตัวอย่างในคำถาม:

numb=1025

Endian น้อย

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

ว้าว! ฉันได้รับผลลัพธ์ที่คาดหวังตามคำอธิบายของ OP!

ปัญหาที่ OP ไม่ได้รับตามที่คาดไว้คือ:

  1. ดูเหมือนว่าเขาไม่ได้เปลี่ยนจาก LSB เป็น MSB

  2. เมื่อขยับบิตในรูปแบบ Little Endian คุณควรตระหนัก (ขอบคุณพระเจ้าที่ฉันรู้) ว่า:

LSB 10000000 00000000 MSB << 1คือ
LSB 00000000 00000001 MSB, ไม่ LSB 01000000 00000000 MSB

เพราะสำหรับแต่ละคน8bitsเรากำลังเขียนมันในMSB 00000000 LSBรูปแบบ Big Endian

มันก็เหมือนกัน

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


เพื่อสรุป:

  1. แม้ว่าการดำเนินการระดับบิตจะกล่าวได้ว่าเป็นนามธรรมที่ถูกตัดออก blablablabla ... เมื่อเราคำนวณการดำเนินงานระดับบิตด้วยมือเรายังคงต้องรู้ว่าเราใช้ endianess อะไรในขณะที่เราเขียนรูปแบบไบนารีลงบนกระดาษ นอกจากนี้เรายังต้องตรวจสอบให้แน่ใจว่าผู้ให้บริการทุกรายใช้ปลายทางเดียวกัน

  2. OP ไม่ได้รับผลลัพธ์ตามที่คาดไว้เพราะเขาทำผิดพลาด

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.