นี่คือลิงค์ไปยังเอกสารของอัลกอริทึมที่สร้างค่าและรหัสที่ฉันเห็นด้วย Visual Studio (ในกรณีส่วนใหญ่) และฉันถือว่ายังคงใช้ใน GCC สำหรับการหารของจำนวนเต็มตัวแปรโดยจำนวนคงที่
http://gmplib.org/~tege/divcnst-pldi94.pdf
ในบทความ uword มี N bits, udword มี 2N bits, n = ตัวเศษ = เงินปันผล, d = ตัวหาร = ตัวหาร, initially ถูกตั้งค่าเริ่มต้นเป็น ceil (log2 (d)), shpre ถูก pre-shift (ใช้ก่อนทวีคูณ ) = e = จำนวนของศูนย์ zero bits ใน d, shpost คือ post-shift (ใช้หลังจากคูณ), prec คือความแม่นยำ = N - e = N - shpre เป้าหมายคือเพิ่มประสิทธิภาพการคำนวณของ n / d โดยใช้ pre-shift, multiply และ post-shift
เลื่อนลงไปที่รูป 6.2 ซึ่งกำหนดวิธีสร้างตัวคูณ udword (ขนาดสูงสุดคือ N + 1 บิต) แต่ไม่อธิบายกระบวนการอย่างชัดเจน ฉันจะอธิบายด้านล่างนี้
รูปที่ 4.2 และรูปที่ 6.2 แสดงว่าตัวคูณสามารถลดลงเป็นตัวคูณ N หรือน้อยกว่าสำหรับตัวหารส่วนใหญ่ได้อย่างไร สมการ 4.5 อธิบายวิธีที่สูตรที่ใช้จัดการกับตัวคูณ N + 1 บิตในรูปที่ 4.1 และ 4.2
ในกรณีของตัวประมวลผล X86 ที่ทันสมัยและตัวประมวลผลอื่นการคูณเวลาได้รับการแก้ไขดังนั้น pre-shift ไม่ได้ช่วยตัวประมวลผลเหล่านี้ แต่ก็ยังช่วยลดตัวคูณจาก N + 1 บิตเป็นบิต N ฉันไม่รู้ว่า GCC หรือ Visual Studio กำจัด pre-shift สำหรับเป้าหมาย X86 หรือไม่
กลับไปที่รูปที่ 6.2 ตัวหาร (เงินปันผล) สำหรับ mlow และ mhigh สามารถมากกว่า udword เฉพาะเมื่อตัวหาร (ตัวหาร)> 2 ^ (N-1) (เมื่อℓ == N => mlow = 2 ^ (2N) ในกรณีนี้ การแทนที่ที่เหมาะสมที่สุดสำหรับ n / d เป็นการเปรียบเทียบ (ถ้า n> = d, q = 1, อื่น ๆ q = 0) ดังนั้นจึงไม่มีการสร้างตัวคูณ ค่าเริ่มต้นของ mlow และ mhigh จะเป็น N + 1 บิตและสามารถแบ่งใช้ udword / uword ได้สองตัวเพื่อสร้างค่า N + 1 บิตแต่ละตัว (mlow หรือ mhigh) การใช้ X86 ในโหมด 64 บิตเป็นตัวอย่าง:
; upper 8 bytes of dividend = 2^(ℓ) = (upper part of 2^(N+ℓ))
; lower 8 bytes of dividend for mlow = 0
; lower 8 bytes of dividend for mhigh = 2^(N+ℓ-prec) = 2^(ℓ+shpre) = 2^(ℓ+e)
dividend dq 2 dup(?) ;16 byte dividend
divisor dq 1 dup(?) ; 8 byte divisor
; ...
mov rcx,divisor
mov rdx,0
mov rax,dividend+8 ;upper 8 bytes of dividend
div rcx ;after div, rax == 1
mov rax,dividend ;lower 8 bytes of dividend
div rcx
mov rdx,1 ;rdx:rax = N+1 bit value = 65 bit value
คุณสามารถทดสอบสิ่งนี้กับ GCC คุณได้เห็นวิธีการจัดการ j = i / 5 ดูวิธีจัดการ j = i / 7 (ซึ่งควรเป็นกรณีตัวคูณทวีคูณ N + 1 บิต)
ในโปรเซสเซอร์ปัจจุบันส่วนใหญ่ทวีคูณมีกำหนดเวลาที่แน่นอนดังนั้นจึงไม่จำเป็นต้องเปลี่ยนล่วงหน้า สำหรับ X86 ผลลัพธ์สุดท้ายคือชุดคำสั่งสองชุดสำหรับตัวหารส่วนใหญ่และชุดคำสั่งห้าชุดสำหรับตัวหารเช่น 7 (เพื่อจำลองตัวคูณ N + 1 บิตดังแสดงในสมการ 4.5 และรูปที่ 4.2 ของไฟล์ pdf) ตัวอย่างรหัส X86-64:
; rax = dividend, rbx = 64 bit (or less) multiplier, rcx = post shift count
; two instruction sequence for most divisors:
mul rbx ;rdx = upper 64 bits of product
shr rdx,cl ;rdx = quotient
;
; five instruction sequence for divisors like 7
; to emulate 65 bit multiplier (rbx = lower 64 bits of multiplier)
mul rbx ;rdx = upper 64 bits of product
sub rbx,rdx ;rbx -= rdx
shr rbx,1 ;rbx >>= 1
add rdx,rbx ;rdx = upper 64 bits of corrected product
shr rdx,cl ;rdx = quotient
; ...