นี่คือลิงค์ไปยังเอกสารของอัลกอริทึมที่สร้างค่าและรหัสที่ฉันเห็นด้วย 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
;       ...