Entwickler-Ecke

Algorithmen, Optimierung und Assembler - Optimierung: Zerlegung von Fliesskommazahl in int und real


Spaceguide - Fr 19.08.05 13:50
Titel: Optimierung: Zerlegung von Fliesskommazahl in int und real
Gerade bei Grafikprogrammierung braucht man oft eine Funktion, die eine Fliesskommazahl in einen Integer-Wert und den Wert "hinter dem Komma" zerlegt. Um Funktionsaufrufe gering zu halten, habe ich folgendes zusammengepfuscht:

const value_shifter : double = -0.5;


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
procedure FracTruncXYZ(const x,y,z : double; var ix,iy,iz: integer; var fx,fy,fz: double);
var i: integer;
begin
 asm
      fld      x
      fadd     value_shifter
      fistp    i
 end;

 ix := i;
 fx := x - i;

 asm
      fld      y
      fadd     value_shifter
      fistp    i
 end;

 iy := i;
 fy := y - i;

 asm
      fld      z
      fadd     value_shifter
      fistp    i
 end;

 iz := i;
 fz := z - i;
end;


Da kann man doch bestimmt noch einiges rausholen, oder?


Allesquarks - Fr 19.08.05 17:38

Da Du Value Shifter dreimal verwendest würde ich ihn für besser auf dem FPU-Stack halten. Da Du ja sonst drei Speicherzugriffe brauchst.
fx = x - i = x - (x + valueshifter) = -valueshifter = 0,5 ??????? Und das dreimal!! Vielleicht hab ich aber auch was übersehen!

P.S. Bekommst Du Überhaupt die Ergebnisse, die Du haben willst?


Spaceguide - Sa 20.08.05 19:37

Ja das Teil funktioniert schon, zumindest bei positiven Zahlen.


BenBE - So 21.08.05 10:44

Ihr meint sowas hier?


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
type
    TFloatValue = packed record
        Int: Extended;
        Float: Extended;
    end;

Function SplitInt(Var X: Extended): TFloatValue;
asm
    FLD     TBYTE PTR [X]

    PUSH    $1F72   //0001 [11]11 0111 0010   //Round control to truncate fraction

    FLD     ST(0)
    FNSTCW  WORD PTR [ESP+2]
    FLDCW   WORD PTR [ESP]
    FRNDINT
    FLDCW   WORD PTR [ESP+2]
    POP     ECX

    FLD     ST(0)
    FSTP    TBYTE PTR [Result.TFloatValue.Int]
    FSUBP
    FSTP    TBYTE PTR [Result.TFloatValue.Float]
end;


Für den speziellen Fall auch:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
type
    TFloatValue = packed record
        Int: Integer;
        Float: Extended;
    end;

Function SplitInt(Var X: Extended): TFloatValue;
asm
    FLD     TBYTE PTR [X]

    PUSH    $1F72   //0001 [11]11 0111 0010   //Round control to truncate fraction

    FLD     ST(0)
    FNSTCW  WORD PTR [ESP+2]
    FLDCW   WORD PTR [ESP]
    FRNDINT

    FIST    DWORD PTR [Result.TFloatValue.Int]
    FSUBP
    FSTP    TBYTE PTR [Result.TFloatValue.Float]

    FLDCW   WORD PTR [ESP+2]
    POP     ECX
end;


Erstere Variante ist getestet und funzt. Auch für negative Zahlen. Jedoch wird der Nachkommateil bei Negativen Zahlen auch negativ angegeben. Um dies zu beheben, braucht man einfach hinter dem FSUBP ein FABS zu ergänzen oder Bits 6 & 7 des ersten Bytes in Float auf 0 zu setzen. Da man aber eh auf die FPU warten muss, ist erstere Möglichkeit IMHO schneller.


Spaceguide - So 21.08.05 17:13

Gibt's das auch für Double-Precision? Mit Extended ist es langsamer als meine Routine.


BenBE - So 21.08.05 21:48

Jup. Müsste ich mir nochmal angucken, geht aber sicherlich.

Meine Routinen vermeiden Integer-Überläufe, da der Integer in der FPU gehalten wird.

Da Du das aber für Batch-Verarbeitung zu nutzen scheinst, wäre sicherlich die Verwendung von SIMD (z.B. SSE oder SSE2) eine Idee. Allerdings geht bei SSE nur Single-Precision zu verwenden, bei SSE2 zwar auch Dbl, aber nur halbe Datenmenge. Da ich aber mit beidem noch nicht gearbeitet hab, müsste ich das anderen überlassen.