>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
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ň