Burada, Visual Studio ile gördüğüm değerleri ve kodu üreten bir algoritmanın belgesine bağlantı (çoğu durumda) ve hala bir değişken tamsayının sabit bir tamsayı ile bölünmesi için GCC'de kullanıldığını varsayıyorum.
http://gmplib.org/~tege/divcnst-pldi94.pdf
Makalede, bir uword'de N bitleri vardır, bir udword'de 2N bitleri vardır, n = pay = temettü, d = payda = bölen, ℓ başlangıçta tavana (log2 (d)) ayarlanmıştır, shpre önceden kaydırılır (çarpmadan önce kullanılır) ) = e = d cinsinden sondaki sıfır bit sayısı, shpost kaydırma sonrası (çarpma işleminden sonra kullanılır), prec kesinlik = N - e = N - shpre'dir. Amaç, vardiya öncesi, çarpma ve vardiya sonrası n / d hesaplamasını optimize etmektir.
Bir udword çarpanının (maksimum boyut N + 1 bittir) nasıl oluşturulduğunu tanımlayan, ancak işlemi açıkça açıklamayan şekil 6.2'ye gidin. Bunu aşağıda açıklayacağım.
Şekil 4.2 ve şekil 6.2, çarpanların çoğu için çarpanın N bit veya daha az çarpanına nasıl indirgenebileceğini göstermektedir. Denklem 4.5, şekil 4.1 ve 4.2'deki N + 1 bit çarpanlarıyla uğraşmak için kullanılan formülün nasıl türetildiğini açıklar.
Modern X86 ve diğer işlemciler durumunda, çarpma süresi sabittir, bu nedenle ön kaydırma bu işlemcilerde yardımcı olmaz, ancak yine de çarpanı N + 1 bitlerinden N bitlerine indirmeye yardımcı olur. GCC veya Visual Studio'nun X86 hedefleri için ön kaydırmayı ortadan kaldırıp kaldırmadığını bilmiyorum.
Şekil 6.2'ye dönme. Mlow ve mhigh için pay (temettü) yalnızca bir paydadan daha büyük olabilir (payda (bölen)> 2 ^ (N-1) (ℓ == N => mlow = 2 ^ (2N)), bu durumda n / d için optimize edilmiş değiştirme bir karşılaştırmadır (n> = d, q = 1, başka q = 0 ise), bu nedenle çarpan oluşturulmaz. Mlow ve mhigh'in başlangıç değerleri N + 1 bit olacaktır ve her N + 1 bit değerini (mlow veya mhigh) üretmek için iki udword / uword bölmesi kullanılabilir. Örnek olarak X86'yı 64 bit modunda kullanma:
; 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
Bunu GCC ile test edebilirsiniz. J = i / 5'in nasıl ele alındığını zaten gördünüz. J = i / 7'nin nasıl ele alındığına bir göz atın (N + 1 bit çarpan durumu olmalıdır).
Mevcut işlemcilerin çoğunda, çarpma işleminin sabit bir zamanlaması vardır, bu nedenle bir ön kaydırma gerekmez. X86 için, sonuç çoğu bölen için iki komut dizisi ve 7 gibi bölücüler için beş komut dizisidir (denklem 4.5 ve pdf dosyasının şekil 4.2'sinde gösterildiği gibi bir N + 1 bit çarpanı taklit etmek için). Örnek X86-64 kodu:
; 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
; ...