>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Fixed-point <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< Fixed point je geniální způsob, jak se vyhnout zdlouhavým operacím v plovoucí čárce. Jde v podstatě o to, vynásobit číslo, se kterým budeme pracovat dostatečně velkou konstantou tak, aby poskytovala dostatek námi požadované desetinné přesnosti, poté s ním pracujeme a pak, když potřebujeme použít reálnou hodnotu, vydělíme ho zase tou konstantou. V praxi se pak pro násobení používají SHL a pro dělení SHR. Zkusím příklad - klasický interpolovací algoritmus barev po lajně (Gouraud): Floating-point ^^^^^^^^^^^^^^ procedure Interpoluj (X1,X2,Y,Barva1,Barva2:nejakej_typ) var step:real; actua:real; ofs,i:word; { step = (Barva2-Barva1)/(X2-X1); actua = Barva1; ofs = (Y << 8) + (Y << 6); for i = X1 to X2 do { mem[seg:ofs+i] = round(actua); actua = actua + step; } } Fixed-point ^^^^^^^^^^^ procedure Interpoluj (X1,X2,Y,Barva1,Barva2:nejakej_typ) var step:word nebo longint; actua: - " - ; ofs,i:word; { step := ((Barva2-Barva1)*256) DIV (X2-X1); /*A je to fixed point !*/ actua := Barva1; ofs := (Y << 8) + (Y << 6); for i = X1 to X2 do { mem[seg:ofs] := actua DIV 256; actua := actua + step; } } Fixed-point-more-optimised ^^^^^^^^^^^^^^^^^^^^^^^^^^ procedure Interpoluj (X1,X2,Y,Barva1,Barva2:nejakej_celoc_typ) var step:word nebo longint; actua: - " - ; ofs,i:word; { step := ((Barva2-Barva1) << 8) DIV (X2-X1); /*rychleji a rychleji :)*/ actua := Barva1; ofs := (Y << 8) + (Y << 6); for i = X1 to X2 do { mem[seg:ofs+i] := actua >> 8; actua := actua + step; } } A teď ASM - algoritmus je optimalizován POUZE na fixed-point a párování instrukcí pro CPU Pentáč : In : BX = X1 CX = X2 AH = C2 // barva v bode X2 AL = C1 // barva v bode X1 DI = Y Code: ... Sub cx, bx Mov si, ax ; v pohodě Sub ah, al Xor dx,dx ; taky Xor al, al Div cx ; ne - div neni jednoduchá instrukce Xchg ax, dx Mov ax, di ; Ne - čtení po zápisu (nelze) Shl ax, 8 Shl di, 6 ; pohoda Add ax, bx Mov bx, si ; o5 párujeme Add di, ax Mov bx, si ; není problém Shl bx, 8 ; hmmm ? zbyla sama ? možná, že tam kompiler (nebo my) hodí NOP :-) @loop: Mov al,bh Mov [di],al ; párkujeme Inc di Add bx,dx ; znova Dec cx jnz short @loop ; a zase Tyto algoritmy jsou pouze ukázkové - nezaručuji funkčnost, protože bylo použito fixed-point 8.8 a Xové souřadnice by mohly hravě "přetéct" (a navíc jsem ten algoritmus nezkoušel :-] ). V praxi se nejčastěji používají tyto kombinace fixed-point : +------+-----------+-----------+---------+-------------+---------------+ |typ | Celá | Deset. | | | | |FixPnt| část | část | 22/7 ** | Přesnost | Odchylka | +------+-----------+-----------+---------+-------------+---------------+ | 8.8 | 256 | 256 | 805 | 3.14453125 | -0.001674107 *| |10.6 | 1 024 | 64 | 201 | 3.140625 | 0.002232142 | |6.10 | 64 | 1024 | 3218 | 3.142578125 | 0.000279017 | |16.16 | 65 536 | 65 536 | 205970 | 3.142852783 | 0.000004359 | |24.8 |16 777 216 | 256 | 805 | 3.14453125 | -0.001674107 *| |8.24 | 256 |16 777 216 |52728393 | 3.142857134 | 8.85 x 10-3 | +------+-----------+-----------+---------+-------------+---------------+ * Hodnota je větší v důsledku zaokrouhlování při převádění real na fixed ** Pro příklad bylo použito "náhodného" zlomku 22/7, což je 3.142857143. Tento sloupec ukazuje, jak vypadá zlomek 22/7 ve fixed-point. Samozřejmě Vám nikdo nezakazuje více exotické kombinace jako 2.30 , nebo 22.10 a tak. Záleží čistě na tom, jak velkou přesnost potřebujete, ale je třeba odhadnout rozsah celé části tak, aby vám ta celá část "nepřetekla" (overflow). výheň