อ่าน 4 นาที
การเลื่อนเลขคณิต
ใน การเขียนโปรแกรมคอมพิวเตอร์ การ เลื่อนเลขคณิต (arithmetic shift) คือ ตัวดำเนินการเลื่อน (shift operator ) ซึ่งบางครั้งเรียกว่า การเลื่อนแบบมีเครื่องหมาย (signed shift )...
การเลื่อนเลขคณิต


| ภาษาหรือตัวประมวลผล | ซ้าย | ขวา |
|---|---|---|
| ActionScript 3, Java , JavaScript , Python , PHP , Ruby , C , C++ , [ 1 ] D , C# , Go , Julia , Rust (เฉพาะประเภทที่มีเครื่องหมาย), [ 2 ] Swift (เฉพาะประเภทที่มีเครื่องหมาย) [หมายเหตุ 1 ] | << | >> |
| อาดา | Shift_Left [ 3 ] | Shift_Right_Arithmetic |
| โคทลิน | shl | shr |
| ฟอร์ทราน | SHIFTL | SHIFTA [หมายเหตุ 2 ] |
| มาตรฐาน ML | << | ~>> |
| เวริล็อก | <<< | >>> [หมายเหตุ 3 ] |
| ภาษามาโคร OpenVMS | @ [หมายเหตุ 4 ] | |
| โครงการ | arithmetic-shift[หมายเหตุ 5 ] | |
| ลิสปาร์กทั่วไป | ash | |
| โอแคมล์ | lsl | asr |
| ฮัสเคลล์ | Data.Bits.shift[หมายเหตุ 6 ] | |
| วีเอชดีแอล | sla[หมายเหตุ 7 ] | sra |
| การประกอบ: Z80 | SLA[ 5 ] | SRA |
| การประกอบ: x86 | SAL | SAR |
| การประกอบ: 68k | ASL | ASR |
| ภาษาแอสเซมบลี: RISC-V | sll[ 6 ]slli | sra,srai |
ในการเขียนโปรแกรมคอมพิวเตอร์การเลื่อนเลขคณิต (arithmetic shift)คือตัวดำเนินการเลื่อน (shift operator ) ซึ่งบางครั้งเรียกว่าการเลื่อนแบบมีเครื่องหมาย (signed shift ) (แม้ว่าจะไม่จำกัดเฉพาะตัวถูกดำเนินการที่มีเครื่องหมายก็ตาม) ประเภทพื้นฐานสองประเภทคือการเลื่อนเลขคณิตไปทางซ้ายและการเลื่อนเลขคณิตไปทางขวาสำหรับเลขฐานสองมันเป็นการดำเนินการระดับบิตที่เลื่อนบิตทั้งหมดของตัวถูกดำเนินการ โดยแต่ละบิตในตัวถูกดำเนินการจะถูกเลื่อนไปตามจำนวนตำแหน่งบิตที่กำหนด และตำแหน่งบิตที่ว่างจะถูกเติมเต็ม แทนที่จะถูกเติมด้วย 0 ทั้งหมด เหมือนกับการเลื่อนเชิงตรรกะเมื่อเลื่อนไปทางขวา บิตซ้ายสุด (โดยปกติคือบิตเครื่องหมายในจำนวนเต็มที่ มีเครื่องหมาย) จะถูกทำซ้ำเพื่อเติมเต็มตำแหน่งที่ว่างทั้งหมด (นี่คือ การขยายเครื่องหมายชนิดหนึ่ง)
ผู้เขียนบางคนนิยมใช้คำว่าsticky right-shiftและzero-fill right-shiftสำหรับการเลื่อนเลขคณิตและการเลื่อนตรรกะตามลำดับ[ 7 ]
การเลื่อนเลขคณิตมีประโยชน์ในฐานะวิธีการที่มีประสิทธิภาพในการคูณหรือหารจำนวนเต็มที่มีเครื่องหมายด้วยกำลังของสอง การเลื่อนไปทางซ้ายnบิตในเลขฐานสองที่มีเครื่องหมายหรือไม่มีเครื่องหมายจะมีผลเป็นการคูณด้วย 2n การเลื่อนไปทางขวาnบิตใน เลขฐาน สองที่มีเครื่องหมายแบบสองคอมพลีเมนต์จะมีผลเป็นการหารด้วย 2n แต่จะปัดลงเสมอ (ไปทางลบอนันต์) ซึ่งแตกต่างจากวิธีการปัดเศษที่มักทำในการหารจำนวนเต็มที่มีเครื่องหมาย (ซึ่งปัดไปทาง 0) ความแตกต่างนี้ทำให้เกิดบั๊กในคอมไพเลอร์จำนวนมาก[ 8 ]
ตัวอย่างเช่น ในชุดคำสั่ง x86คำสั่ง SAR (arithmetic right shift) จะหารจำนวนที่มีเครื่องหมายด้วยกำลังของสอง โดย ปัดเศษเข้าหาค่าลบอนันต์[ 9 ]อย่างไรก็ตาม คำสั่ง IDIV (signed divide) จะหารจำนวนที่มีเครื่องหมาย โดยปัดเศษเข้าหาศูนย์ ดังนั้น คำสั่ง SAR จึงไม่สามารถใช้แทนคำสั่ง IDIV ด้วยกำลังของสองได้ และในทางกลับกันก็เช่นกัน
คำจำกัดความอย่างเป็นทางการ
นิยามอย่างเป็นทางการของการเลื่อนทางคณิตศาสตร์ ตามมาตรฐานของรัฐบาลกลาง 1037Cคือ:
การเลื่อนบิตทางคณิตศาสตร์ คือการเลื่อนบิตที่ใช้กับการแสดงตัวเลขใน ระบบเลข ฐาน คงที่ และ ระบบเลข ทศนิยมคงที่โดยจะเลื่อนเฉพาะอักขระที่แสดงส่วนทศนิยมคงที่ของตัวเลขเท่านั้น การเลื่อนบิตทางคณิตศาสตร์มักเทียบเท่ากับการคูณตัวเลขด้วยเลขยกกำลังจำนวนเต็มบวกหรือลบของฐาน ยกเว้นผลของการปัดเศษ โปรดเปรียบเทียบการเลื่อนบิตเชิงตรรกะกับการเลื่อนบิตทางคณิตศาสตร์ โดยเฉพาะอย่างยิ่งในกรณีของการแสดงตัวเลขทศนิยมลอยตัว
คำสำคัญในคำจำกัดความของ FS 1073C คือ "โดยปกติ"
ความไม่เท่ากันของการเลื่อนบิตไปทางขวาและการหารทางคณิตศาสตร์
อย่างไรก็ตาม การเลื่อนบิต ไปทางขวา ทางคณิตศาสตร์ เป็นกับดักสำคัญสำหรับผู้ที่ไม่ระมัดระวัง โดยเฉพาะอย่างยิ่งในการจัดการกับการปัดเศษจำนวนเต็มลบ ตัวอย่างเช่น ใน การแสดงจำนวนเต็มลบ แบบสองคอมพลีเมนต์ตามปกติ −1 จะถูกแทนด้วยเลข 1 ทั้งหมด สำหรับจำนวนเต็มแบบมีเครื่องหมาย 8 บิต จะได้เป็น 1111 1111 การเลื่อนบิตไปทางขวาทางคณิตศาสตร์ 1 ตำแหน่ง (หรือ 2, 3, ..., 7) จะได้ 1111 1111 อีกครั้ง ซึ่งก็ยังคงเป็น −1 นี่สอดคล้องกับการปัดเศษลง (ไปทางลบอนันต์) แต่ไม่ใช่หลักการทั่วไปสำหรับการหาร
มีการกล่าวกันบ่อยครั้งว่าการเลื่อนบิตไปทางขวาแบบเลขคณิตเทียบเท่ากับการหารด้วยกำลังของฐาน (บวก จำนวนเต็ม) (เช่น การหารด้วยกำลังของ 2 สำหรับเลขฐานสอง) และด้วยเหตุนี้ การหารด้วยกำลังของฐานจึงสามารถปรับให้เหมาะสมที่สุดได้โดยการนำไปใช้เป็นการเลื่อนบิตไปทางขวาแบบเลขคณิต (ตัวเลื่อนบิตนั้นง่ายกว่าตัวหารมาก บนโปรเซสเซอร์ส่วนใหญ่ คำสั่งเลื่อนบิตจะทำงานได้เร็วกว่าคำสั่งหาร) คู่มือการเขียนโปรแกรม คู่มือ และข้อกำหนดอื่นๆ จำนวนมากในช่วงทศวรรษ 1960 และ 1970 จากบริษัทและสถาบันต่างๆ เช่นDEC , IBM , Data GeneralและANSIระบุข้อความที่ไม่ถูกต้องดังกล่าว [ 10 ]
การเลื่อนบิตไปทางขวาเชิงตรรกะเทียบเท่ากับการหารด้วยกำลังของฐาน (โดยปกติคือ 2) เฉพาะสำหรับจำนวนบวกหรือจำนวนที่ไม่มีเครื่องหมายเท่านั้น การเลื่อนบิตไปทางขวาเชิงเลขคณิตเทียบเท่ากับการเลื่อนบิตไปทางขวาเชิงตรรกะสำหรับจำนวนบวกที่มีเครื่องหมาย การเลื่อนบิตไปทางขวาเชิงเลขคณิตสำหรับจำนวนลบในระบบเลขฐาน N (โดยปกติ คือ ระบบเลขฐานสอง ) เทียบเท่ากับการหารด้วยกำลังของฐาน (โดยปกติคือ 2) โดยประมาณ โดยสำหรับจำนวนคี่จะปัดลง (ไม่ใช่ปัดเข้าหา 0 อย่างที่คาดไว้โดยทั่วไป)
การเลื่อนบิตไปทางขวาสำหรับจำนวนลบนั้นเทียบเท่ากับการหารโดยใช้การปัดเศษเข้าหา 0 ใน การแสดงจำนวนที่มีเครื่องหมายในรูปแบบส่วนเติม เต็มหนึ่ง (ones' complement)ซึ่งเป็นวิธีการที่ใช้ในคอมพิวเตอร์รุ่นเก่าบางเครื่อง แต่ปัจจุบันไม่นิยมใช้แล้ว
การจัดการปัญหาในภาษาโปรแกรม
มาตรฐาน ISO (1999) สำหรับภาษาการเขียนโปรแกรมCกำหนดตัวดำเนินการเลื่อนขวาโดยพิจารณาจากการหารด้วยกำลังของ 2 [ 11 ]เนื่องจากความไม่เท่าเทียมกันที่กล่าวไว้ข้างต้น มาตรฐานจึงยกเว้นการเลื่อนขวาของจำนวนที่มีเครื่องหมายซึ่งมีค่าเป็นลบออกจากคำจำกัดความนั้นโดยชัดเจน ไม่ได้ระบุพฤติกรรมของตัวดำเนินการเลื่อนขวาในกรณีดังกล่าว แต่กำหนดให้คอมไพเลอร์ C แต่ละตัวต้องกำหนดพฤติกรรมของการเลื่อนค่าลบไปทางขวา[หมายเหตุ 8 ]
เช่นเดียวกับ C, C++ มีการเลื่อนขวาที่กำหนดโดยการใช้งานสำหรับจำนวนเต็มที่มีเครื่องหมายจนถึงC++20เริ่มตั้งแต่มาตรฐาน C++20 การเลื่อนขวาของจำนวนเต็มที่มีเครื่องหมายถูกกำหนดให้เป็นการเลื่อนเลขคณิต[ 13 ]
แอปพลิเคชัน
ในการใช้งานที่ต้องการการปัดเศษลงอย่างสม่ำเสมอ การเลื่อนบิตไปทางขวาแบบเลขคณิตสำหรับค่าที่มีเครื่องหมายจะมีประโยชน์ ตัวอย่างเช่น ในการลดขนาดพิกัดแรสเตอร์ลงด้วยกำลังของสอง ซึ่งจะรักษาระยะห่างที่สม่ำเสมอ ตัวอย่างเช่น การเลื่อนบิตไปทางขวา 1 ตำแหน่ง จะส่งค่า 0, 1, 2, 3, 4, 5, ... ไปยัง 0, 0, 1, 1, 2, 2, ... และค่า −1, −2, −3, −4, ... ไปยัง −1, −1, −2, −2, ... โดยคงระยะห่างที่เท่ากันไว้ คือ −2, −2, −1, −1, 0, 0, 1, 1, 2, 2, ... ในทางตรงกันข้าม การหารจำนวนเต็มโดยปัดเศษเข้าหาศูนย์ จะส่งค่า −1, 0 และ 1 ไปยัง 0 (3 จุดแทนที่จะเป็น 2 จุด) ทำให้ได้ค่า −2, −1, −1, 0, 0, 0, 1, 1, 2, 2, ... แทน ซึ่งมีระยะห่างไม่สม่ำเสมอที่ 0
ดูเพิ่มเติม
หมายเหตุ
- ^ตัว
>>ดำเนินการในภาษา C และ C++ ไม่จำเป็นต้องเป็นการเลื่อนบิตทางคณิตศาสตร์เสมอไป โดยปกติแล้วจะเป็นการเลื่อนบิตทางคณิตศาสตร์ก็ต่อเมื่อใช้กับชนิดข้อมูลจำนวนเต็มที่มีเครื่องหมายทางด้านซ้ายมือเท่านั้น หากใช้กับชนิดข้อมูลจำนวนเต็มที่ไม่มีเครื่องหมาย จะเป็นการเลื่อนบิตเชิงตรรกะ - ^ฟอร์ทราน 2008
- ^ตัวดำเนินการเลื่อนบิตขวาแบบเลขคณิตใน Verilog จะทำการเลื่อนบิตแบบเลขคณิตก็ต่อเมื่อตัวถูกดำเนินการตัวแรกเป็นจำนวนเต็มบวกหรือลบเท่านั้น หากตัวถูกดำเนินการตัวแรกเป็นจำนวนเต็มลบหรือบวก ตัวดำเนินการจะทำการเลื่อนบิตขวาแบบตรรกะแทน
- ^ใน ภาษามาโคร OpenVMSการเลื่อนเลขคณิตไปทางซ้ายหรือขวาจะถูกกำหนดโดยว่าตัวถูกดำเนินการตัวที่สองเป็นบวกหรือลบ ซึ่งเป็นเรื่องผิดปกติ ในภาษาโปรแกรมส่วนใหญ่ ทิศทางทั้งสองจะมีตัวดำเนินการที่แตกต่างกัน โดยตัวดำเนินการจะระบุทิศทาง และตัวถูกดำเนินการตัวที่สองจะเป็นบวกโดยปริยาย (บางภาษา เช่น Verilog กำหนดให้ค่าลบต้องแปลงเป็นค่าบวกที่ไม่มีเครื่องหมาย บางภาษา เช่น C และ C++ ไม่มีพฤติกรรมที่กำหนดไว้หากใช้ค่าลบ) [ 4 ]
- ^ในภาษา Scheme
arithmetic-shiftสามารถเป็นการเลื่อนซ้ายและขวาได้ ขึ้นอยู่กับตัวถูกดำเนินการตัวที่สอง คล้ายกับภาษามาโครของ OpenVMS มาก แม้ว่า R6RS Scheme จะเพิ่มทั้งสอง-rightเข้า-leftมาก็ตาม - ^คลาส
BitsจากData.Bitsโมดูลของ Haskell กำหนดทั้งshiftการรับอาร์กิวเมนต์แบบมีเครื่องหมายและshiftLแบบshiftRไม่มีเครื่องหมาย ซึ่งทั้งสองแบบนี้มีความ สัมพันธ์กัน (isomorphic ) สำหรับการกำหนดนิยามใหม่ โปรแกรมเมอร์จำเป็นต้องระบุเพียงรูปแบบใดรูปแบบหนึ่งเท่านั้น และอีกรูปแบบหนึ่งจะถูกกำหนดโดยอัตโนมัติโดยอิงจากรูปแบบที่ระบุ - ^ตัวดำเนินการเลื่อนบิตซ้ายทางคณิตศาสตร์ของ VHDL นั้นผิดปกติ แทนที่จะเติมบิตที่มีค่าต่ำสุด (LSB) ของผลลัพธ์ด้วยศูนย์ มันจะคัดลอก LSB เดิมไปยัง LSB ใหม่ แม้ว่านี่จะเป็นภาพสะท้อนที่เหมือนกันทุกประการของการเลื่อนบิตขวาทางคณิตศาสตร์ แต่มันไม่ใช่คำจำกัดความตามปกติของตัวดำเนินการ และไม่เทียบเท่ากับการคูณด้วยกำลังของ 2 ในมาตรฐาน VHDL 2008 พฤติกรรมที่แปลกประหลาดนี้ยังคงไม่เปลี่ยนแปลง (เพื่อความเข้ากันได้กับเวอร์ชันก่อนหน้า) สำหรับประเภทอาร์กิวเมนต์ที่ไม่มีการตีความเชิงตัวเลขแบบบังคับ (เช่น BIT_VECTOR) แต่ 'SLA' สำหรับ ประเภทอาร์กิวเมนต์ แบบไม่มีเครื่องหมายและมีเครื่องหมายจะทำงานในลักษณะที่คาดหวัง (เช่น ตำแหน่งขวาสุดจะถูกเติมด้วยศูนย์) ฟังก์ชัน shift left logical (SLL) ของ VHDL นั้นได้นำการเลื่อนบิตทางคณิตศาสตร์ 'มาตรฐาน' ดังกล่าวมาใช้
- ^มาตรฐาน C มีจุดประสงค์เพื่อไม่จำกัดภาษา C ไว้เฉพาะสถาปัตยกรรมแบบ one's complement หรือ two's complement เท่านั้น ในกรณีที่พฤติกรรมของการแสดงผลแบบ one's complement และ two's complement แตกต่างกัน เช่นในกรณีนี้ มาตรฐานกำหนดให้คอมไพเลอร์ C แต่ละตัวต้องจัดทำเอกสารเกี่ยวกับพฤติกรรมของสถาปัตยกรรมเป้าหมายของตน ตัวอย่างเช่น เอกสารประกอบสำหรับ GNU Compiler Collection (GCC) ระบุถึงพฤติกรรมของการใช้การขยายเครื่องหมาย [ 12 ]
สรุปเนื้อหา
ข้อมูลสำคัญจากบทความ
ข้อมูลสำคัญเกี่ยวกับ การเลื่อนเลขคณิต
ใน การเขียนโปรแกรมคอมพิวเตอร์ การ เลื่อนเลขคณิต (arithmetic shift) คือ ตัวดำเนินการเลื่อน (shift operator ) ซึ่งบางครั้งเรียกว่า การเลื่อนแบบมีเครื่องหมาย (signed shift )...
คำจำกัดความอย่างเป็นทางการ
นิยามอย่างเป็นทางการของการเลื่อนทางคณิตศาสตร์ ตาม มาตรฐานของรัฐบาลกลาง 1037C คือ:
ความไม่เท่ากันของการเลื่อนบิตไปทางขวาและการหารทางคณิตศาสตร์
อย่างไรก็ตาม การเลื่อนบิต ไปทางขวา ทางคณิตศาสตร์ เป็นกับดักสำคัญสำหรับผู้ที่ไม่ระมัดระวัง โดยเฉพาะอย่างยิ่งในการจัดการกับการปัดเศษจำนวนเต็มลบ ตัวอย่างเช่น ใน การแสดงจำนวนเต็มลบ แบบสอง คอมพลีเมนต์ตามปกติ −1 จะถูกแทนด้วยเลข 1 ทั้งหมด...
แอปพลิเคชัน
ในการใช้งานที่ต้องการการปัดเศษลงอย่างสม่ำเสมอ การเลื่อนบิตไปทางขวาแบบเลขคณิตสำหรับค่าที่มีเครื่องหมายจะมีประโยชน์ ตัวอย่างเช่น ใน การลดขนาด พิกัดแรสเตอร์ลงด้วยกำลังของสอง ซึ่งจะรักษาระยะห่างที่สม่ำเสมอ ตัวอย่างเช่น การเลื่อนบิตไปทางขวา 1 ตำแหน่ง จะส่งค่า 0,...