Seite 3 von 5

Re: Probleme mit Doubles

Verfasst: 27.01.2013 22:51
von STARGÅTE
Da das Problem scheinbar gelöst ist, möchte ich trotzdem noch mal auf folgendes hinweisen:
captain_hesse hat geschrieben:und die Diskriminante für die PQ-Formel auch den wert 0 hat was sie bei einer Tangenten ja auch haben soll
Dieses Verhalten kann von Fließkommazahlen nicht erwartet werden!
Das Ergebnis einer Operation hängt bei Fließkommazahlen nämlich zusätzlich von der Größenordnung der Operanten ab.

Hier mal ein eindrucksvolles Beispiel:
Wenn ich bei 0.0 bin und mich nach vorne bewege und dann wieder mit gleichen Maßen zurück, müsste ich ja wieder bei 0.0 ankommen oder?
Leider nicht! Je nach dem, ob ich +1 und -1 rechne oder +10 und -10, ergibt die Addition von 0.3 im zweiten Schritt ein andere Ergebnis, was zu einer Differenz führt (ist bei Doubles auch, nur in einer andere "Präzision"):

Code: Alles auswählen

Position.f = 0.0
Position + 1.0
Position + 0.3 
Position - 1.0
Position - 0.3 
Debug Position

Position.f = 0.0
Position + 10.0
Position + 0.3 
Position - 10.0
Position - 0.3
Debug Position
Hier eine Abfrage wie: If Position > 0.0 zu machen oder gar auf = 0.0 zu Prüfen wäre also Problematisch.
Ein Grund ist natürlich, dass 0.3 nicht exakt dargestellt werden kann!

Ich kann also nur dazu Raten, eine gewisse Ungenauigkeit einzuplanen, und nicht Sachen wie Position = 0.0 oder so zu nutzen, sondern lieber Abs(Position) < #Epsilon, wobei #Epsilon den Grad der Ungenauigkeit angibt.

Re: Probleme mit Doubles

Verfasst: 27.01.2013 23:21
von captain_hesse
Es ist mir bewusst das Floats bzw. Doubles so gut wie nie genau 0 sind, für diesen Zweck habe ich mir aber eine Funktion geschrieben die mir die Werte ab einer bestimmten Anzahl an Nachkommastellen rundet.

Code: Alles auswählen

Procedure.d runde(v1.d)                                                     
  
  If Abs(v1)<0.0001 : v1=0 :EndIf
  ProcedureReturn v1.d
  
EndProcedure  
Das funktioniert bisher ganz gut.

Re: Probleme mit Doubles

Verfasst: 28.01.2013 22:03
von Helle
Ein bissel spät (mangels Zeit), aber jetzt :D : Der "Fehler" tritt nur in der 32-Bit-Version auf, weil die 64-Bit-(Windows)-DDLs (benutzt von OpenWindowedScreen()) für Gleitkomma-Berechnungen SSE(2) benutzen (ADD, SUB, MUL, DIV, SQR) und nicht die FPU. Damit besteht für 64-Bit keine Notwendigkeit, die FPU zu "frisieren". Für 32-Bit bringt dies aber tatsächlich was (hatte ich vor Jahren hier mal gezeigt). Hintergrund: Die FPU wird mit Double Extended Precision (80-Bit) initialisiert (beim Einschalten, F(N)INIT), Windows (x86/x64) setzt dies auf Double Precision (64-Bit, Process/Thread). Diese Precision-Einstellung, eingestellt im FPU Control-Register (Bit 8 und 9), wird von Windows beibehalten, egal, ob Single (32-Bit)- oder Double (64-Bit)-Precision berechnet werden soll. Programme können dies aber verändern, um mehr Speed rauszuholen. Wird für Single-Precision im Programm die FPU-Precision von Double (eben eigentlich die Dauer-Einstellung) auf Single geändert, geht die Sache flotter zur Sache. Mist ist nur, wenn dies nicht wieder zurückgestellt wird.
Hier zur Erläuterung einige Beispiele:

Code: Alles auswählen

;- PB v5.00 (x64)  

;- FPU-CONTROL-REGISTER:
;      +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+       
;  Bit | 15 | 14 | 13 | 12 | 11 | 10 | 9  | 8  | 7  | 6  | 5  | 4  | 3  | 2  | 1  | 0  | 
;      +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+
;      |              | IC |   RC    |   PC    |    |    | P  | U  | O  | Z  | D  | I  |
;      +--------------+----+---------+---------+----+----+----+----+----+----+----+----+ 
;- PC: Precision Control; aktuell eingestellte Genauigkeit:
;      00b = 0 = Single Precision
;      01b = 1 = Reserviert
;      10b = 2 = Double Precision
;      11b = 3 = Double Extended Precision

CWO.w                        ;FPU_Control_Word_Original     
CWN.w                        ;FPU_Control_Word_Neu
AF.f = 12345.0               ;Variablen für Tests 
BF.f = 67890.0
CF.f
EF.f
AD.d = 12345.0
BD.d = 67890.0
CD.d
ED.d

Procedure SetSingleFloat()
  !fstcw [v_CWO]             ;Control-Word sichern
  !mov ax,[v_CWO]
  !and ax,1111110011111111b  ;die beiden Präzisions-Bits auf %00 setzen = Single-Precision
  !mov [v_CWN],ax
  !fldcw [v_CWN]             ;verändertes Control-Word zurückschreiben
EndProcedure 

Procedure SetDoubleFloat()
  !fstcw [v_CWO]             ;Control-Word sichern
  !mov ax,[v_CWO]
  !and ax,1111110011111111b  ;die beiden Präzisions-Bits löschen (falls es z.B. Double Extended Precision = %11 war)
  !or  ax,0000001000000000b  ;die beiden Präzisions-Bits auf %10 setzen = Double-Precision
  !mov [v_CWN],ax
  !fldcw [v_CWN]             ;verändertes Control-Word zurückschreiben
EndProcedure 

Procedure RestoreCW()
!fldcw [v_CWO]               ;ursprüngliches Control-Word zurückschreiben
EndProcedure 

;-------- Info aktuelle Einstellung
!fstcw [v_CWO]
Prec.i = (CWO >> 8) & %11
Select Prec
  Case 0
    P$ = "Single"
  Case 2
    P$ = "Double"
  Case 3
    P$ = "Double Extended"
EndSelect
MessageRequester("Aktuelle Einstellung FPU-Precision", P$ + "-Precision")

;-------- Test mit "Original"-Einstellungen, z.B. direkt nach PB-Start
Time = ElapsedMilliseconds() ;reicht für diese Demo aus
For i = 0 To 99999999
 CF = AF * BF                ;irgendeine Rechnerei
 EF = Sqr(CF / AF + BF)
Next 
Time1 = ElapsedMilliseconds() - Time 
S1$ = "Test Original Single-Precision:  " + Str(Time1) + " ms = " + StrF(EF) + #LFCR$
MessageRequester("Test Single-Precision Original", S1$)

;-------- Test mit Setzen auf Single-Precision
SetSingleFloat()
Time = ElapsedMilliseconds()
For i = 0 To 99999999
 CF = AF * BF                ;irgendeine Rechnerei
 EF = Sqr(CF / AF + BF)      ;manche Rechenoperationen bringen viel, manche weniger, richten aber keinen Schaden an!
Next 
Time2 = ElapsedMilliseconds() - Time 
S2$ = "Test Single-Precision gesetzt:  " + Str(Time2) + " ms = " + StrF(EF) + #LFCR$
MessageRequester("Test Single-Precision gesetzt", S1$ + S2$)
;spätestens jetzt müsste die Ausgangsstellung wieder hergestellt werden, für nachfolgenden Test aber lassen wir es

;-------- Test mit Double, FPU noch mit Einstellung Single-Precision
Time = ElapsedMilliseconds()
For i = 0 To 99999999
 CD = AD * BD                ;irgendeine Rechnerei
 ED = Sqr(CD / AD + BD)
Next 
Time3 = ElapsedMilliseconds() - Time 
S3$ = "Test Double, Single gesetzt:    " + Str(Time3) + " ms = " + StrD(ED) + #LFCR$
MessageRequester("Test Double-Precision mit Single-Precision gesetzt", S1$ + S2$ + S3$)
RestoreCW()                  ;spätestens jetzt wieder Ausgangsstellung herstellen

;-------- Test mit Double, FPU mit Einstellung Double-Precision
SetDoubleFloat()
Time = ElapsedMilliseconds()
For i = 0 To 99999999
 CD = AD * BD                ;irgendeine Rechnerei
 ED = Sqr(CD / AD + BD)
Next 
Time4 = ElapsedMilliseconds() - Time 
S4$ = "Test Double, Double gesetzt:  " + Str(Time4) + " ms = " + StrD(ED)
MessageRequester("Test Double-Precision mit Double-Precision gesetzt", S1$ + S2$ + S3$ + S4$)
RestoreCW()                  ;wieder Ausgangsstellung herstellen

Viel Spaß!
Helle

Re: Probleme mit Doubles

Verfasst: 28.01.2013 22:40
von NicTheQuick
Cooler Test. Bei mir kommt folgendes 'raus:

Code: Alles auswählen

Typ  Precision    Zeit     Ergebnis
.f   Double Ext   5169 ms  368.4833679199
.f   Single       2282 ms  368.4833679199
.d   Single       2224 ms  368.4833679199
.d   Double       4383 ms  368.48337818699
Eigentlich fehlt noch Double mit "Double Extension"-Precision, aber man sieht wirklich sehr schön die Zeitunterschiede.

Re: Probleme mit Doubles

Verfasst: 28.01.2013 22:51
von STARGÅTE
komisch, bei mir sind die Unterschiede kleiner:

Code: Alles auswählen

---------------------------
Test Double-Precision mit Double-Precision gesetzt
---------------------------
Test Original Single-Precision:  1295 ms = 368.4833679199

Test Single-Precision gesetzt:  920 ms = 368.4833679199

Test Double, Single gesetzt:    920 ms = 368.4833679199

Test Double, Double gesetzt:  1295 ms = 368.4833781869
---------------------------
OK   
---------------------------

Re: Probleme mit Doubles

Verfasst: 28.01.2013 22:56
von NicTheQuick
Bei mir läuft der erste Test mit "Double Extension", also 80 Bit. Deswegen dauert der am längsten. Bei dir scheint Standardmäßig "nur" Double-Precision eingestellt zu sein.

Aber trotzdem faszinierend wie viel schneller deine CPU das macht. :D Bei mir läuft es unter Linux x64 und mit 2 GHz auf einem Core.

Re: Probleme mit Doubles

Verfasst: 28.01.2013 23:01
von ts-soft
Das liegt wohl an der CPU, bzw ob die 64 oder 32 bit nutzt.
32-Bit
Test Double-Precision mit Double-Precision gesetzt hat geschrieben:---------------------------
---------------------------
Test Original Single-Precision: 1388 ms = 368.4833679199

Test Single-Precision gesetzt: 1014 ms = 368.4833679199

Test Double, Single gesetzt: 2153 ms = 368.4833679199

Test Double, Double gesetzt: 2605 ms = 368.4833781869
---------------------------
OK
---------------------------
64-Bit
Test Double-Precision mit Double-Precision gesetzt hat geschrieben:---------------------------
---------------------------
Test Original Single-Precision: 1451 ms = 368.4833679199

Test Single-Precision gesetzt: 1046 ms = 368.4833679199

Test Double, Single gesetzt: 1045 ms = 368.4833679199

Test Double, Double gesetzt: 1482 ms = 368.4833781869
---------------------------
OK
---------------------------

Re: Probleme mit Doubles

Verfasst: 29.01.2013 00:10
von Helle
Wow, Danke für´s Feedback! Vorab, bei mir sieht es so aus:

i7-3770K@4.1GHz, Win7/64:
Aktuell: Double-Precision
Test Original Single-Precision: 702 ms = 368.4833679199
Test Single-Precision gesetzt: 343 ms = 368.4833679199
Test Double, Single gesetzt: 359 ms = 368.4833679199
Test Double, Double gesetzt: 702 ms = 368.4833781869

Pentium-M@1.86GHz, WinXP/32:
Aktuell: Double-Precision
Test Original Single-Precision: 4781 ms = 368.4833679199
Test Single-Precision gesetzt: 2609 ms = 368.4833679199
Test Double, Single gesetzt: 2609 ms = 368.4833679199
Test Double, Double gesetzt: 4766 ms = 368.4833781869

Unter Windows 32/64 sollte es (theoretisch!) so aussehen, das Test1 und Test4 sowie Test2 und Test3 jeweils in etwa gleiche Zeiten benötigen.
@Nic: Linux lässt Extended Double als Standard? Interessant, wusste ich nicht (wird Zeit für mich...).
@STARGÅTE: Sicher auch CPU-abhängig, aber die Tendenz ist doch ok.
@TS: Deine 32-Bit-Werte ähneln denen, die ich unter 64-Bit in einer VM mit XP/32 erhalte. Deshalb habe ich mein altes Notebook (s.o.) mit (echtem) XP rausgekramt.
Danke!
Helle

Re: Probleme mit Doubles

Verfasst: 29.01.2013 00:19
von STARGÅTE
Als Zusatz für meinen Test: Ich habe ein AMD Phenom II X4 955 Prozessor mit 3,21GHz

Re: Probleme mit Doubles

Verfasst: 29.01.2013 00:30
von ts-soft
AMD Athlon X2, 3,1 GHz, muß ich wohl mal aufrüsten, mir fehlen ja ~180 ms <)