Eine Lookup-Table ist natürlich speed-mäßig unschlagbar, das Problem dürfte aber immer das Verhältnis Genauigkeit (Auflösung ) - Table-Größe sein. Wenn für eine Sinus-Berechnung der Radiant so im Bereich zwischen -Pi/2 und +Pi/2 liegt, kann auch Folgendes mal getestet werden (eine CPU mit SSE-Unterstützung setze ich mal voraus):
Code: Alles auswählen
;- Sinus-Berechnung (Single-Precision) mit SSE im Bereich -90° bis +90°
;- Verwendet wird die Reihenentwicklung Sin(x) = x - (x^3)/3! + (x^5)/5! - (x^7)/7! + (x^9)/9!...
;- die Glieder 2-5 können in einem SSE-Register berechnet werden (1 Float-Wert = 32-Bit, 1 SSE-Register = 128-Bit)
;- "Helle" Klaus Helbing, 22.01.2010, PB v4.40 x86, Windows32
Global Radiant.f = 0.987654 ;hier Testwert, je nach erforderlicher Genauigkeit Werte zwischen -Pi/2 und +Pi/2 sinnvoll; testen!
Global SinPB.f
Global SinSSE.f
Procedure SinSSE(Radiant, SinSSE) ;oder Parameter (auch Anzahl!) anpassen an eigenes Programm, hier eigentlich doppelt gemoppelt
!lea eax,[v_Radiant] ;Speicher-Adresse von Radiant
!movss xmm0,[eax] ;1 = x; 1 = unterstes Double-Word des XMM-Registers, 2 = 2.Double-Word usw.
!shufps xmm0,xmm0,0 ;1 - 4 = x
!movaps xmm2,xmm0 ;x sichern (1.Glied)
!movaps xmm1,xmm0
!mulss xmm1,xmm1 ;1-1-1-2 Exponenten-Belegung des Zielregisters
!mulps xmm0,xmm0 ;2-2-2-2
!mulps xmm0,xmm1 ;3-3-3-4
!movlhps xmm0,xmm1 ;1-2-3-4
!mulps xmm0,xmm0 ;2-4-6-8
!mulps xmm0,xmm2 ;3-5-7-9 also oberstes Double-Word = x^3, unterstes = x^9
!lea edx,[v_RezFak] ;Anfangs-Speicher-Adresse der reziproken Fakultäten
!movups xmm1,[edx]
!mulps xmm0,xmm1 ;Multiplikation der 4 Power-Werte mit den reziproken Fakultäten
!movaps xmm1,xmm0
!shufps xmm1,xmm1,11100101b ;die einzelnen Glieder in unterstes Double-Word des SSE-Registers schieben
!addss xmm0,xmm1 ;und addieren (HADDPS in SSE3)
!shufps xmm1,xmm1,11100110b
!addss xmm0,xmm1
!shufps xmm1,xmm1,11100111b
!addss xmm0,xmm1
!addss xmm0,xmm2 ;zum Schluss 1.Glied addieren (x)
!lea eax,[v_SinSSE]
!movss [eax],xmm0 ;im untersten DWord von XMM0 steht nun das Ergebnis
EndProcedure
;----------------------------------------------------------
;- Structure für SSE --------------------------------------
Structure Sinus
RF1.f : RF2.f : RF3.f : RF4.f
EndStructure
Define.Sinus RezFak
RezFak\RF1 = 2.755731922398589065e-6 ; 1/9!
RezFak\RF2 = -1.984126984126984127e-4 ;-1/7!
RezFak\RF3 = 8.333333333333333333e-3 ; 1/5!
RezFak\RF4 = -1.666666666666666667e-1 ;-1/3!
;----------------------------------------------------------
;- Test SSE
T1= ElapsedMilliseconds()
For i = 0 To 19999999
SinSSE(Radiant, SinSSE)
Next
T1 = ElapsedMilliseconds() - T1
;- Test PB
T2= ElapsedMilliseconds()
For i = 0 To 19999999
SinPB = Sin(Radiant)
Next
T2 = ElapsedMilliseconds() - T2
Sin$ = "Test für Radiant = " + StrF(Radiant) + #CRLF$ + #CRLF$
Sin$ + "SSE : " + StrF(SinSSE) + " in " + Str(T1) + " ms" + #CRLF$
Sin$ + "PB : " + StrF(SinPB) + " in " + Str(T2) + " ms" + #CRLF$
MessageRequester("Sinus Single-Precision", Sin$)
Soll nur eine Anregung sein

!
Schreibt man ein Programm nur mit Single-Precision und vielen Divisionen (nicht jede Division lässt sich leicht durch eine Multiplikation ersetzen) , kann auch Folgendes ausprobiert werden:
Code: Alles auswählen
B.f = 5
C.f = 0.2
;- althergebracht
time = ElapsedMilliseconds()
For n = 1 To 10000000
A1.f = 1/B/B/B/B
Next
time1 = ElapsedMilliseconds()-time
time = ElapsedMilliseconds()
For n = 1 To 10000000
A2.f = 1*C*C*C*C
Next
time2 = ElapsedMilliseconds()-time
;MessageRequester("", Str(time1)+" zu "+Str(time2)+#CRLF$+StrF(A1)+" zu "+StrF(A2))
;- und hier der Test mit zwangsweisem Single-Precision-Test
Global CWO.w ;Originales Control-Word
Global CWN.w ;Neues Control-Word
Procedure SetSingleFloat()
!fstcw [v_CWO] ;Original Control-Word sichern
!mov ax,[v_CWO]
!and ax,1111110011111111b ;die 2 Precisions-Bits auf Null setzen = Single-Float
!mov [v_CWN],ax
!fldcw [v_CWN] ;das neue Control-Word zurückschreiben
EndProcedure
Procedure RestoreCW()
!fldcw [v_CWO] ;das originale Control-Word wieder zurückschreiben
EndProcedure
time = ElapsedMilliseconds()
SetSingleFloat()
For n = 1 To 10000000
A3.f = 1/B/B/B/B
Next
time3 = ElapsedMilliseconds()-time
time = ElapsedMilliseconds()
For n = 1 To 10000000
A4.f = 1*C*C*C*C
Next
RestoreCW()
time4 = ElapsedMilliseconds()-time
MessageRequester("", "Althergebracht: "+Str(time1)+" zu "+Str(time2)+" " + StrF(A1)+" zu "+StrF(A2)+#CRLF$+"Test: "+Str(time3)+" zu "+Str(time4)+" " + StrF(A3)+" zu "+StrF(A4))
Dies orientiert sich an obigem Beispiel und soll auch nur für Testzwecke (nicht nur Divisionen) sein!
Gruß
Helle