Berikut ini tautan ke dokumen algoritme yang menghasilkan nilai dan kode yang saya lihat dengan Visual Studio (dalam kebanyakan kasus) dan yang saya asumsikan masih digunakan dalam GCC untuk pembagian bilangan variabel dengan bilangan bulat konstan.
http://gmplib.org/~tege/divcnst-pldi94.pdf
Dalam artikel tersebut, sebuah uword memiliki N bit, udword memiliki 2N bit, n = pembilang = dividen, d = penyebut = pembagi, ℓ awalnya diatur ke ceil (log2 (d)), shpre adalah pra-shift (digunakan sebelum dikalikan ) = e = jumlah trailing zero bits dalam d, shpost adalah post-shift (digunakan setelah multiply), prec presisi = N - e = N - shpre. Tujuannya adalah untuk mengoptimalkan perhitungan n / d menggunakan pre-shift, multiply, dan post-shift.
Gulir ke bawah ke gambar 6.2, yang mendefinisikan bagaimana pengganda kata kunci (ukuran maksimum adalah N + 1 bit), dihasilkan, tetapi tidak jelas menjelaskan prosesnya. Saya akan jelaskan di bawah ini.
Gambar 4.2 dan Gambar 6.2 menunjukkan bagaimana pengali dapat dikurangi menjadi N bit atau kurang pengali untuk sebagian besar pembagi. Persamaan 4.5 menjelaskan bagaimana rumus yang digunakan untuk menangani pengganda N + 1 bit pada gambar 4.1 dan 4.2 diturunkan.
Dalam kasus X86 modern dan prosesor lainnya, waktu penggandaan tetap, jadi pra-shift tidak membantu pada prosesor ini, tetapi masih membantu mengurangi pengganda dari N + 1 bit ke N bit. Saya tidak tahu apakah GCC atau Visual Studio telah menghilangkan pra-shift untuk target X86.
Kembali ke Gambar 6.2. Pembilang (dividen) untuk mlow dan mhigh dapat lebih besar dari udword hanya ketika penyebut (pembagi)> 2 ^ (N-1) (ketika ℓ == N => mlow = 2 ^ (2N)), dalam hal ini penggantian yang dioptimalkan untuk n / d adalah perbandingan (jika n> = d, q = 1, jika tidak q = 0), maka tidak ada pengali yang dihasilkan. Nilai awal mlow dan mhigh akan menjadi N + 1 bit, dan dua pembagian udword / uword dapat digunakan untuk menghasilkan setiap nilai bit N + 1 (mlow atau mhigh). Menggunakan X86 dalam mode 64 bit sebagai contoh:
; 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
Anda dapat menguji ini dengan GCC. Anda sudah melihat bagaimana j = i / 5 ditangani. Lihatlah bagaimana j = i / 7 ditangani (yang seharusnya merupakan kasus pengganda N + 1 bit).
Pada sebagian besar prosesor saat ini, multiply memiliki timing yang tetap, sehingga pra-shift tidak diperlukan. Untuk X86, hasil akhirnya adalah dua urutan instruksi untuk sebagian besar pembagi, dan urutan lima instruksi untuk pembagi seperti 7 (untuk meniru suatu pengganda bit N + 1 seperti yang ditunjukkan dalam persamaan 4.5 dan gambar 4.2 dari file pdf). Contoh kode 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
; ...