我想將 ax 暫存器中的一個無符號 8.8 定點數與 1.00125 相乘和除,并將結果也存盤在 ax 中。
我知道定點乘法/除法需要一些額外的步驟,但我不知道如何在匯編中實作這些步驟。
非常感謝幫助。
uj5u.com熱心網友回復:
如果您關心準確性,則 1.00125 不能完全以任何整數格式或任何浮點格式存盤,因為它是二進制的遞回分數(在二進制中,它是1.000000000101000111101011100001010001111010111...b該00001010001111010111序列永遠重復的地方)。出于這個原因,我會將其轉換為有理數 801/800;然后做x * 1.00125 = (x * 801) / 800(可能在除法上使用“舍入到最近”)。
如果您不關心準確性,那么您可以為“1.00125”使用的位數越多,結果就越接近正確答案。使用 8 位(“1.7 定點”),您可以獲得的最接近的是 1.0000000b,這意味著您可以跳過乘法 ( x * 1.00125 = x)。使用 16 位(“1.15 定點”),您可以獲得的最接近的是 1.000000000101001b(或十進制的 1.001220703125)。
但是,您可以作弊更多。具體來說,您可以通過執行(x * 1) (x * 0.00125). 例如,不是像 1.000000000101001b(其中 9 位是零)這樣的 16 位常量,而是可以有像 0.0000000001010001111010111b 這樣的 16 位常量(其中 16 位是沒有任何前導零的最后 16 位)。在這種情況下,常數非常接近(例如 0.00124999880)而不是“不太接近”(例如 1.001220703125)。
諷刺地; 只有 16 位,這個“0.00125”比 1.00125 的 32 位浮點表示更準確。
所以..在匯編中(假設一切都是未簽名的)它可能看起來像:
;ax = x << 8 (or x as an 8.8 fixed point number)
mov cx,ax ;cx = x << 8
mov bx,41943 ;bx = 41943 = 0.00124999880 << 25
mul bx ;dx:ax = (x << 8) * (0.00124999880 << 25) = x * 0.00124999880 << 33
;dx = x * 0.00124999880 << 17
shr dx,9 ;dx = x * 0.00124999880 << 17 >> 9 = x * 0.00124999880 << 8, carry flag = last bit shifted out
adc dx,0 ;Round up to nearest (add 1 if last bit shifted out was set)
lea ax,[dx cx] ;ax = x << 8 x * 0.00124999880 << 8 = x * 1.00124999880 << 8
當然,這里的問題是將其轉換回“8.8 定點”無論如何都會破壞大部分準確性。為了保持大部分精度,您可以使用 32 位結果(“8.24 定點”)來代替。這可能看起來像:
;ax = x << 8 (or x as an 8.8 fixed point number)
mov cx,ax ;cx = x << 8
mov bx,41943 ;bx = 41943 = 0.00124999880 << 25
mul bx ;dx:ax = (x << 8) * (0.00124999880 << 25) = x * 0.00124999880 << 33
add ax,1 << 8 ;To cause the following shift to round to nearest
adc dx,0
shrd ax,dx,9
shr dx,9 ;dx:ax = x * 0.00124999880 << 33 >> 0 = x * 0.00124999880 << 24
;cx:0 = x << 24
add dx,cx ;dx:ax = x << 24 x * 0.00124999880 << 24 = x * 1.00124999880 << 24
另一個問題是潛在的溢位。例如,如果x是 0xFF.FF(或大約 255.996),結果將類似于 256.32,它太大而無法放入“8.8”或“8.24”或“8.anything”定點格式。為了避免這個問題,您可以增加整數位數(并將精度降低 1 位) - 例如使結果為“9.7 定點”或“9.23 定點”。
這里的要點是:
a) 對于“定點”計算,每次運算(乘法、除法、加法等)都會導致小數點移動。
b) 由于小數點一直在移動,所以最好對每一步的小數點位置采用標準表示法。我的方法是在注釋中包含一個“顯式轉換”(例如“x << 8”而不僅僅是“x”)。這種“注釋中記錄的顯式移位”可以輕松確定小數點移動的位置,以及是否/需要向左/向右移動多少才能轉換為不同的定點格式。
c) 對于好的代碼,你需要注意準確性和溢位,這會導致小數點移動得更多(并且使得使用“小數點所在位置的標準表示法”更重要)。
uj5u.com熱心網友回復:
一個簡單的解決方案是僅使用 x87 浮點單元進行乘法運算。假設使用 nasm 的實模式(未經測驗):
example:
push bp
mov sp, bp ; establish stack frame
push ax
push ax ; make space for quotient
fild word [bp-2] ; load number
fld st0 ; duplicate top of stack
fmul dword [factor] ; compute product
fistp word [bp-2]
fmul dword [invfac] ; compute quotient
fistp word [bp-4]
pop dx ; quotient
pop ax ; product
pop bp ; tear down stack framt
ret
factor dd 1.00125
invfac dd 0.999875 ; 1/1.00125
這將商留在 中dx,乘積在 中ax。舍入是根據 x87 FPU 中配置的舍入模式完成的(默認情況下應該舍入到最接近的)。
uj5u.com熱心網友回復:
關于定點乘法要理解的一件事是結果的點是運算元 1 的點加上運算元 2 的點。
因此,當兩個數相乘的定點為零時,我們得到定點為零的結果。
當兩個數字相乘時,在 8 位(二進制)上有定點,我們得到一個在 16 位有定點的數字。
因此,需要根據需要縮小這樣的結果。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/343356.html
上一篇:運行os.system()時python意外的EOF
下一篇:如何找到矩形內每個點的距離?
