ตัวดำเนินการกะ (<<, >>) เป็นเลขคณิตหรือตรรกะใน C หรือไม่?


140

ใน C ตัวดำเนินการ shift ( <<, >>) เป็นเลขคณิตหรือตรรกะหรือไม่?


1
ความหมายของเลขคณิตและตรรกะคืออะไร? คำถามที่เกี่ยวข้องสำหรับการลงนาม ints: stackoverflow.com/questions/4009885/…
Ciro Santilli 郝海东冠状病六四事件法轮功

คำตอบ:


102

ตามK&R ฉบับที่ 2ผลลัพธ์จะขึ้นอยู่กับการใช้งานสำหรับการเปลี่ยนค่าที่ลงนามอย่างถูกต้อง

Wikipediaกล่าวว่า C / C ++ 'โดยปกติ' จะใช้การเปลี่ยนแปลงทางคณิตศาสตร์กับค่าที่ลงชื่อ

โดยทั่วไปคุณต้องทดสอบคอมไพเลอร์ของคุณหรือไม่ต้องพึ่งพาคอมไพเลอร์ ความช่วยเหลือ VS2008 ของฉันสำหรับคอมไพเลอร์ MS C ++ ปัจจุบันบอกว่าคอมไพเลอร์ของพวกเขาทำการเปลี่ยนเลขคณิต


เกี่ยวกับคำตอบนี้ไม่เพียง แต่คอมไพเลอร์เท่านั้น แต่การรวมกันของสถาปัตยกรรมคอมไพเลอร์และ (โปรเซสเซอร์) จะขึ้นอยู่กับลักษณะการทำงาน
stephan

144

เมื่อเลื่อนไปทางซ้ายจะไม่มีความแตกต่างระหว่างการเลื่อนเลขคณิตและตรรกะ เมื่อเลื่อนไปทางขวาประเภทของการเลื่อนจะขึ้นอยู่กับประเภทของค่าที่เลื่อน

(ในฐานะที่เป็นพื้นหลังสำหรับผู้อ่านที่ไม่คุ้นเคยกับความแตกต่างการเลื่อนไปทางขวาแบบ "ตรรกะ" ทีละ 1 บิตจะเลื่อนบิตทั้งหมดไปทางขวาและเติมบิตทางซ้ายสุดด้วย 0 การเลื่อน "เลขคณิต" จะปล่อยค่าเดิมในบิตทางซ้ายสุด ความแตกต่างกลายเป็นสิ่งสำคัญเมื่อจัดการกับจำนวนลบ)

เมื่อเปลี่ยนค่าที่ไม่ได้ลงชื่อตัวดำเนินการ >> ใน C จะเป็นการกะแบบตรรกะ เมื่อเปลี่ยนค่าที่เซ็นแล้วตัวดำเนินการ >> จะเป็นการกะเลขคณิต

ตัวอย่างเช่นสมมติว่าเป็นเครื่อง 32 บิต:

signed int x1 = 5;
assert((x1 >> 1) == 2);
signed int x2 = -5;
assert((x2 >> 1) == -3);
unsigned int x3 = (unsigned int)-5;
assert((x3 >> 1) == 0x7FFFFFFD);

59
ใกล้แล้ว Greg คำอธิบายของคุณเกือบจะสมบูรณ์แบบ แต่การเปลี่ยนนิพจน์ของประเภทที่ลงนามและค่าเชิงลบจะถูกกำหนดให้ใช้งานได้ ดู ISO / IEC 9899: 1999 หัวข้อ 6.5.7
Robᵩ

12
@Rob: อันที่จริงสำหรับการเลื่อนซ้ายและจำนวนลบที่มีการเซ็นชื่อพฤติกรรมนั้นไม่ได้กำหนดไว้
JeremyP

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

3
@supercat: ฉันไม่รู้จริงๆ อย่างไรก็ตามฉันทราบว่ามีเอกสารกรณีที่โค้ดที่มีพฤติกรรมที่ไม่ได้กำหนดทำให้คอมไพเลอร์ทำสิ่งที่ไม่ใช้งานง่าย (โดยปกติจะเกิดจากการเพิ่มประสิทธิภาพเชิงรุก - ตัวอย่างเช่นดูข้อผิดพลาดตัวชี้ null ของไดรเวอร์ Linux TUN / TAP เก่า: lwn.net / บทความ / 342330 ). ถ้าฉันไม่จำเป็นต้องใส่เครื่องหมายในการเลื่อนด้านขวา (ซึ่งฉันรู้ว่าเป็นพฤติกรรมที่กำหนดการใช้งาน) ฉันมักจะพยายามทำการกะบิตของฉันโดยใช้ค่าที่ไม่ได้ลงชื่อแม้ว่าจะหมายถึงการใช้การร่าย
Michael Burr

2
@MichaelBurr: ฉันรู้ว่าคอมไพเลอร์ไฮเปอร์โมเดิร์นใช้ความจริงที่ว่าพฤติกรรมที่ไม่ได้กำหนดโดยมาตรฐาน C (แม้ว่าจะถูกกำหนดไว้ใน 99% ของการใช้งาน ) เป็นเหตุผลในการเปลี่ยนโปรแกรมที่พฤติกรรมจะถูกกำหนดไว้อย่างสมบูรณ์ แพลตฟอร์มที่พวกเขาสามารถคาดหวังได้ว่าจะทำงานเป็นกลุ่มคำสั่งเครื่องจักรที่ไร้ค่าโดยไม่มีพฤติกรรมที่เป็นประโยชน์ ฉันจะยอมรับว่า (พูดถากถาง) ฉันงงว่าทำไมผู้เขียนคอมไพเลอร์ถึงพลาดโอกาสในการเพิ่มประสิทธิภาพที่ยิ่งใหญ่ที่สุด: ละเว้นส่วนใดส่วนหนึ่งของโปรแกรมซึ่งหากไปถึงจะส่งผลให้ฟังก์ชันซ้อนกัน ...
supercat

52

TL; ดร

พิจารณาiและnเป็นตัวถูกดำเนินการด้านซ้ายและขวาตามลำดับของตัวดำเนินการกะ ประเภทของหลังจากที่โปรโมชั่นจำนวนเต็มเป็นi Tสมมติว่าnอยู่ใน[0, sizeof(i) * CHAR_BIT)- ไม่ได้กำหนดเป็นอย่างอื่น - เรามีกรณีเหล่านี้:

| Direction  |   Type   | Value (i) | Result                   |
| ---------- | -------- | --------- | ------------------------ |
| Right (>>) | unsigned |    ≥ 0    | −∞ ← (i ÷ 2ⁿ)            |
| Right      | signed   |    ≥ 0    | −∞ ← (i ÷ 2ⁿ)            |
| Right      | signed   |    < 0    | Implementation-defined†  |
| Left  (<<) | unsigned |    ≥ 0    | (i * 2ⁿ) % (T_MAX + 1)   |
| Left       | signed   |    ≥ 0    | (i * 2ⁿ) ‡               |
| Left       | signed   |    < 0    | Undefined                |

†คอมไพเลอร์ส่วนใหญ่ใช้สิ่งนี้เป็นกะเลขคณิต
‡ไม่ได้กำหนดหากค่ามากเกินประเภทผลลัพธ์ T; ประเภทที่เลื่อนตำแหน่งของ i


ขยับ

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

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

การเลื่อนเลขคณิตทางซ้ายของจำนวน X โดย n นั้นเทียบเท่ากับการคูณ X ด้วย 2 nและเทียบเท่ากับการเลื่อนซ้ายเชิงตรรกะ การเปลี่ยนแปลงเชิงตรรกะจะให้ผลลัพธ์เช่นเดียวกันเนื่องจาก MSB ตกลงไปจากจุดสิ้นสุดและไม่มีอะไรจะรักษา

การเปลี่ยนเลขคณิตที่ถูกต้องของจำนวน X โดย n จะเทียบเท่ากับการหารจำนวนเต็มของ X โดย 2 nเท่านั้นถ้า X ไม่เป็นลบ! การหารจำนวนเต็มไม่ใช่อะไรนอกจากการหารทางคณิตศาสตร์และปัดเศษเป็น 0 ( trunc )

สำหรับจำนวนลบซึ่งแสดงโดยการเข้ารหัสเสริมของสองตัวการเลื่อนไปทางขวาด้วย n บิตมีผลในทางคณิตศาสตร์หารด้วย 2 nและปัดเศษไปทาง −∞ ( พื้น ) ดังนั้นการขยับที่ถูกต้องจึงแตกต่างกันสำหรับค่าที่ไม่ใช่ค่าลบและค่าลบ

สำหรับ X ≥ 0, X >> n = X / 2 n = trunc (X ÷ 2 n )

สำหรับ X <0, X >> n = ชั้น (X ÷ 2 n )

การ÷หารทางคณิตศาสตร์อยู่ที่ไหนคือการหาร/จำนวนเต็ม ลองดูตัวอย่าง:

37) 10 = 100101) 2

37 ÷ 2 = 18.5

37/2 = 18 (ปัดเศษ 18.5 ไปทาง 0) = 10010) 2 [ผลลัพธ์ของการเลื่อนขวาเลขคณิต]

-37) 10 = 11011011) 2 (พิจารณาส่วนเติมเต็มของสองส่วนการแทนค่า 8 บิต)

-37 ÷ 2 = -18.5

-37 / 2 = -18 (ปัดเศษ 18.5 ไปทาง 0) = 11101110) 2 [ไม่ใช่ผลลัพธ์ของการเลื่อนขวาทางคณิตศาสตร์]

-37 >> 1 = -19 (ปัดเศษ 18.5 ไปทาง −∞) = 11101101) 2 [ผลลัพธ์ของการเลื่อนขวาทางคณิตศาสตร์]

ดังที่Guy Steele ชี้ให้เห็นความแตกต่างนี้ทำให้เกิดข้อบกพร่องในคอมไพเลอร์มากกว่าหนึ่งตัว ที่นี่ที่ไม่ใช่ลบ (คณิตศาสตร์) สามารถจับคู่กับค่าที่ไม่เป็นลบและไม่ได้ลงนามและลงนาม (C); ทั้งสองได้รับการปฏิบัติเหมือนกันและการเลื่อนไปทางขวาทำได้โดยการหารจำนวนเต็ม

ดังนั้นตรรกะและเลขคณิตจึงเทียบเท่ากันในการขยับซ้ายและสำหรับค่าที่ไม่เป็นลบในการขยับขวา มันเป็นการเปลี่ยนค่าเชิงลบที่แตกต่างกันอย่างเหมาะสม

ตัวดำเนินการและประเภทผลลัพธ์

มาตรฐาน C99 §6.5.7 :

แต่ละตัวถูกดำเนินการต้องมีประเภทจำนวนเต็ม

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

short E1 = 1, E2 = 3;
int R = E1 << E2;

ในตัวอย่างข้างต้นตัวถูกดำเนินการทั้งสองจะกลายเป็นint(เนื่องจากการส่งเสริมจำนวนเต็ม) ถ้าE2เป็นลบหรือE2 ≥ sizeof(int) * CHAR_BITไม่ได้กำหนดการดำเนินการ เนื่องจากการขยับมากกว่าบิตที่มีอยู่นั้นจะล้นออกมาอย่างแน่นอน ได้Rรับการประกาศให้เป็นshortที่intผลการดำเนินงานเปลี่ยนแปลงจะถูกแปลงโดยปริยายshort; Conversion ที่แคบลงซึ่งอาจนำไปสู่พฤติกรรมที่กำหนดการนำไปใช้หากไม่สามารถแสดงค่าในประเภทปลายทางได้

Shift ซ้าย

ผลลัพธ์ของ E1 << E2 คือ E1 ตำแหน่งบิต E2 ที่เลื่อนไปทางซ้าย บิตที่ว่างจะเต็มไปด้วยศูนย์ ถ้า E1 มีประเภทที่ไม่ได้ลงนามค่าของผลลัพธ์คือ E1 × 2 E2ลดโมดูโลหนึ่งค่ามากกว่าค่าสูงสุดที่แสดงได้ในประเภทผลลัพธ์ ถ้า E1 มีประเภทที่ลงนามและค่าที่ไม่เป็นลบและ E1 × 2 E2สามารถแสดงได้ในประเภทผลลัพธ์นั่นคือค่าผลลัพธ์ มิฉะนั้นจะไม่มีการกำหนดพฤติกรรม

เนื่องจากการเลื่อนด้านซ้ายเหมือนกันสำหรับทั้งสองบิตที่ว่างจะเต็มไปด้วยศูนย์ จากนั้นระบุว่าสำหรับทั้งประเภทที่ไม่ได้ลงนามและประเภทที่ลงนามแล้วจะเป็นการเปลี่ยนแปลงทางคณิตศาสตร์ ฉันตีความว่ามันเป็นการเลื่อนเลขคณิตเนื่องจากการเปลี่ยนแปลงเชิงตรรกะไม่ได้กังวลเกี่ยวกับค่าที่แทนด้วยบิตเพียงแค่มองว่ามันเป็นกระแสของบิต แต่การเจรจาไม่ได้มาตรฐานในแง่ของบิต แต่ด้วยการกำหนดมันในแง่ของมูลค่าที่ได้จากผลิตภัณฑ์ของ E1 กับ 2 E2

ข้อแม้คือสำหรับประเภทที่ลงนามแล้วค่าควรเป็นค่าที่ไม่เป็นลบและค่าผลลัพธ์ควรเป็นตัวแทนในประเภทผลลัพธ์ มิฉะนั้นการดำเนินการจะไม่ถูกกำหนด ประเภทผลลัพธ์จะเป็นประเภทของ E1 หลังจากใช้การส่งเสริมแบบอินทิกรัลและไม่ใช่ประเภทปลายทาง (ตัวแปรที่จะเก็บผลลัพธ์) ค่าผลลัพธ์จะถูกแปลงเป็นประเภทปลายทางโดยปริยาย หากไม่สามารถแสดงได้ในประเภทนั้นการแปลงจะถูกกำหนดให้ใช้งาน (C99 §6.3.1.3 / 3)

ถ้า E1 เป็นประเภทที่เซ็นชื่อด้วยค่าลบพฤติกรรมของการขยับซ้ายจะไม่ได้กำหนดไว้ นี่เป็นเส้นทางที่ง่ายสำหรับพฤติกรรมที่ไม่ได้กำหนดซึ่งอาจถูกมองข้ามไปได้ง่าย

ขวา Shift

ผลลัพธ์ของ E1 >> E2 คือ E1 ตำแหน่งบิต E2 ที่เลื่อนไปทางขวา ถ้า E1 มีประเภทที่ได้รับการรับรองหรือถ้า E1 มีการลงนามชนิดและค่าที่ไม่ใช่เชิงลบค่าของผลที่ได้เป็นส่วนหนึ่งของความฉลาดของ E1 / 2 E2 ถ้า E1 มีประเภทที่ลงนามและค่าติดลบค่าผลลัพธ์จะถูกกำหนดให้ใช้งานได้

การเลื่อนด้านขวาสำหรับค่าที่ไม่ได้ลงชื่อและไม่ติดลบนั้นค่อนข้างตรงไปตรงมา บิตว่างจะเต็มไปด้วยศูนย์ สำหรับค่าลบที่มีการลงนามผลลัพธ์ของการเปลี่ยนขวาจะถูกกำหนดให้นำไปใช้งาน ที่กล่าวว่าการใช้งานส่วนใหญ่เช่น GCC และVisual C ++ใช้การเปลี่ยนขวาเป็นการเปลี่ยนเลขคณิตโดยการรักษาบิตเครื่องหมาย

สรุป

แตกต่างจาก Java ซึ่งมีตัวดำเนินการพิเศษ>>>สำหรับการเปลี่ยนตรรกะนอกเหนือจากปกติ>>และ<<C และ C ++ มีเฉพาะการเปลี่ยนเลขคณิตโดยมีบางพื้นที่ที่ไม่ได้กำหนดและกำหนดการนำไปใช้งาน เหตุผลที่ฉันถือว่าพวกเขาเป็นเลขคณิตเกิดจากการใช้ถ้อยคำมาตรฐานในการดำเนินการทางคณิตศาสตร์แทนที่จะถือว่าตัวถูกดำเนินการเปลี่ยนเป็นกระแสของบิต นี่อาจเป็นเหตุผลว่าทำไมจึงปล่อยให้พื้นที่เหล่านั้นไม่ถูกกำหนด / กำหนดการนำไปใช้งานแทนที่จะกำหนดเฉพาะกรณีทั้งหมดเป็นการเปลี่ยนแปลงเชิงตรรกะ


1
คำตอบที่ดี เกี่ยวกับการปัดเศษ (ในหัวข้อการเปลี่ยนเกียร์) - เลื่อนไปทางขวา-Infสำหรับทั้งจำนวนลบและบวก ปัดเศษต่อ 0 -Infของจำนวนบวกเป็นกรณีส่วนตัวของปัดเศษต่อ เมื่อตัดทอนคุณจะทิ้งค่าที่ถ่วงน้ำหนักในเชิงบวกเสมอดังนั้นคุณจึงลบออกจากผลลัพธ์ที่แน่นอนเป็นอย่างอื่น
ysap

1
@ysap เย้สังเกตุดีๆ โดยพื้นฐานแล้วการปัดเศษเป็น 0 สำหรับจำนวนบวกเป็นกรณีพิเศษของรอบทั่วไปที่มีต่อ −∞; สิ่งนี้สามารถเห็นได้ในตารางซึ่งทั้งตัวเลขบวกและลบฉันสังเกตว่ามันกลมเข้าหา −∞
legends2k

17

ในแง่ของประเภทของการเปลี่ยนแปลงที่คุณได้รับสิ่งสำคัญคือประเภทของค่าที่คุณกำลังขยับ แหล่งที่มาของจุดบกพร่องแบบคลาสสิกคือเมื่อคุณเลื่อนตัวอักษรเป็นพูดและปิดบังบิต ตัวอย่างเช่นหากคุณต้องการทิ้งจำนวนเต็มทางซ้ายสุดที่ยังไม่ได้ลงชื่อคุณอาจลองใช้เป็นมาสก์:

~0 >> 1

น่าเสียดายที่สิ่งนี้จะทำให้คุณมีปัญหาเนื่องจากมาสก์จะมีการตั้งค่าบิตทั้งหมดไว้เนื่องจากค่าที่ถูกเลื่อน (~ 0) มีการเซ็นชื่อดังนั้นจึงมีการเปลี่ยนเลขคณิต คุณต้องการบังคับให้มีการเปลี่ยนแปลงเชิงตรรกะโดยการประกาศค่าอย่างชัดเจนว่าไม่ได้ลงนามแทนกล่าวคือทำสิ่งนี้:

~0U >> 1;

16

นี่คือฟังก์ชั่นในการรับประกันการเลื่อนขวาเชิงตรรกะและการเลื่อนขวาทางคณิตศาสตร์ของ int ใน C:

int logicalRightShift(int x, int n) {
    return (unsigned)x >> n;
}
int arithmeticRightShift(int x, int n) {
    if (x < 0 && n > 0)
        return x >> n | ~(~0U >> n);
    else
        return x >> n;
}

7

เมื่อคุณทำ - เลื่อนซ้ายด้วย 1 คุณคูณด้วย 2 - กะขวาด้วย 1 คุณหารด้วย 2

 x = 5
 x >> 1
 x = 2 ( x=5/2)

 x = 5
 x << 1
 x = 10 (x=5*2)

ใน x >> a และ x << a ถ้าเงื่อนไขคือ a> 0 ดังนั้นคำตอบคือ x = x / 2 ^ a, x = x * 2 ^ a ตามลำดับแล้วคำตอบจะเป็นอย่างไรถ้า a <0?
JAVA

@sunny: ต้องไม่น้อยกว่า 0 เป็นพฤติกรรมที่ไม่ได้กำหนดใน C.
Jeremy

4

ฉันค้นดูในวิกิพีเดียและพวกเขามีสิ่งนี้ที่จะพูด:

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

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


แม้ว่าคอมไพเลอร์ C ส่วนใหญ่เคยมีการเลื่อนไปทางซ้ายทางคณิตศาสตร์สำหรับค่าที่มีการลงนามพฤติกรรมที่เป็นประโยชน์ดังกล่าวดูเหมือนจะเลิกใช้แล้ว ปรัชญาของคอมไพเลอร์ในปัจจุบันดูเหมือนจะสมมติว่าประสิทธิภาพของการเลื่อนไปทางซ้ายบนตัวแปรทำให้คอมไพเลอร์สมมติว่าตัวแปรนั้นต้องไม่เป็นค่าลบดังนั้นจึงละเว้นโค้ดใด ๆ ที่จำเป็นสำหรับพฤติกรรมที่ถูกต้องหากตัวแปรเป็นค่าลบ .
supercat

0

เลื่อนซ้าย <<

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

แต่ในการเปลี่ยนอย่างถูกต้อง>>เราต้องปฏิบัติตามกฎเพิ่มเติมหนึ่งข้อและกฎนั้นเรียกว่า "sign bit copy" ความหมายของ "sign bit copy" คือถ้าMSBมีการตั้งค่าbit ที่สำคัญที่สุด ( ) จากนั้นหลังจาก shift ขวาอีกครั้งMSBจะถูกตั้งค่าหากรีเซ็ตแล้วจะถูกรีเซ็ตอีกครั้งหมายความว่าถ้าค่าก่อนหน้าเป็นศูนย์หลังจากเปลี่ยนอีกครั้ง บิตเป็นศูนย์ถ้าบิตก่อนหน้าเป็นหนึ่งหลังจากการเปลี่ยนแปลงอีกครั้งหนึ่ง กฎนี้ใช้ไม่ได้กับการเลื่อนซ้าย

ตัวอย่างที่สำคัญที่สุดในการกะทางขวาหากคุณเลื่อนจำนวนลบไปเป็นการกะทางขวาหลังจากนั้นค่าบางส่วนก็จะถึงศูนย์แล้วหลังจากนี้ถ้าเลื่อนค่านี้ -1 จำนวนครั้งใด ๆ ค่าจะยังคงเหมือนเดิม โปรดตรวจสอบ.


0

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

จะใช้สิ่งนี้เมื่อทำได้เนื่องจากคอมไพเลอร์อื่น ๆ น่าจะทำ



-7

ตามมากมาย คอมไพเลอร์:

  1. << คือกะซ้ายเลขคณิตหรือกะซ้ายแบบบิต
  2. >> คือตัวเลื่อนขวาทางคณิตศาสตร์

3
"การเลื่อนขวาทางเลขคณิต" และ "การเลื่อนทางขวาแบบบิต" นั้นแตกต่างกัน นั่นคือประเด็นของคำถาม คำถามถามว่า "เป็น>>เลขคณิตหรือบิต (ตรรกะ)" คุณตอบว่า " >>เป็นเลขคณิตหรือบิต" นั่นไม่ตอบคำถาม
wchargin

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