Probleme mit Doubles

Anfängerfragen zum Programmieren mit PureBasic.
Benutzeravatar
STARGÅTE
Kommando SG1
Beiträge: 7031
Registriert: 01.11.2005 13:34
Wohnort: Glienicke
Kontaktdaten:

Re: Probleme mit Doubles

Beitrag 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.
PB 6.01 ― Win 10, 21H2 ― Ryzen 9 3900X, 32 GB ― NVIDIA GeForce RTX 3080 ― Vivaldi 6.0 ― www.unionbytes.de
Aktuelles Projekt: Lizard - Skriptsprache für symbolische Berechnungen und mehr
Benutzeravatar
captain_hesse
Beiträge: 138
Registriert: 17.05.2009 18:55
Computerausstattung: Windows 7 Ultimate 64 Bit / AMD Phenom II 1090T, 6x3200 MHz / AMD HD-6850 / PureBasic 5.1 (x86) (x64)
Wohnort: Saarland

Re: Probleme mit Doubles

Beitrag 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.
Windows 7 Ultimate 64 Bit / AMD Phenom II 1090T, 6x3200 MHz / AMD HD-6850 / PureBasic 5.1 (x86) (x64)
Benutzeravatar
Helle
Beiträge: 566
Registriert: 11.11.2004 16:13
Wohnort: Magdeburg

Re: Probleme mit Doubles

Beitrag 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
Benutzeravatar
NicTheQuick
Ein Admin
Beiträge: 8809
Registriert: 29.08.2004 20:20
Computerausstattung: Ryzen 7 5800X, 64 GB DDR4-3200
Ubuntu 24.04.2 LTS
GeForce RTX 3080 Ti
Wohnort: Saarbrücken

Re: Probleme mit Doubles

Beitrag 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.
Benutzeravatar
STARGÅTE
Kommando SG1
Beiträge: 7031
Registriert: 01.11.2005 13:34
Wohnort: Glienicke
Kontaktdaten:

Re: Probleme mit Doubles

Beitrag 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   
---------------------------
PB 6.01 ― Win 10, 21H2 ― Ryzen 9 3900X, 32 GB ― NVIDIA GeForce RTX 3080 ― Vivaldi 6.0 ― www.unionbytes.de
Aktuelles Projekt: Lizard - Skriptsprache für symbolische Berechnungen und mehr
Benutzeravatar
NicTheQuick
Ein Admin
Beiträge: 8809
Registriert: 29.08.2004 20:20
Computerausstattung: Ryzen 7 5800X, 64 GB DDR4-3200
Ubuntu 24.04.2 LTS
GeForce RTX 3080 Ti
Wohnort: Saarbrücken

Re: Probleme mit Doubles

Beitrag 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.
Benutzeravatar
ts-soft
Beiträge: 22292
Registriert: 08.09.2004 00:57
Computerausstattung: Mainboard: MSI 970A-G43
CPU: AMD FX-6300 Six-Core Processor
GraKa: GeForce GTX 750 Ti, 2 GB
Memory: 16 GB DDR3-1600 - Dual Channel
Wohnort: Berlin

Re: Probleme mit Doubles

Beitrag 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
---------------------------
PureBasic 5.73 LTS | SpiderBasic 2.30 | Windows 10 Pro (x64) | Linux Mint 20.1 (x64)
Nutella hat nur sehr wenig Vitamine. Deswegen muss man davon relativ viel essen.
Bild
Benutzeravatar
Helle
Beiträge: 566
Registriert: 11.11.2004 16:13
Wohnort: Magdeburg

Re: Probleme mit Doubles

Beitrag 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
Benutzeravatar
STARGÅTE
Kommando SG1
Beiträge: 7031
Registriert: 01.11.2005 13:34
Wohnort: Glienicke
Kontaktdaten:

Re: Probleme mit Doubles

Beitrag von STARGÅTE »

Als Zusatz für meinen Test: Ich habe ein AMD Phenom II X4 955 Prozessor mit 3,21GHz
PB 6.01 ― Win 10, 21H2 ― Ryzen 9 3900X, 32 GB ― NVIDIA GeForce RTX 3080 ― Vivaldi 6.0 ― www.unionbytes.de
Aktuelles Projekt: Lizard - Skriptsprache für symbolische Berechnungen und mehr
Benutzeravatar
ts-soft
Beiträge: 22292
Registriert: 08.09.2004 00:57
Computerausstattung: Mainboard: MSI 970A-G43
CPU: AMD FX-6300 Six-Core Processor
GraKa: GeForce GTX 750 Ti, 2 GB
Memory: 16 GB DDR3-1600 - Dual Channel
Wohnort: Berlin

Re: Probleme mit Doubles

Beitrag von ts-soft »

AMD Athlon X2, 3,1 GHz, muß ich wohl mal aufrüsten, mir fehlen ja ~180 ms <)
PureBasic 5.73 LTS | SpiderBasic 2.30 | Windows 10 Pro (x64) | Linux Mint 20.1 (x64)
Nutella hat nur sehr wenig Vitamine. Deswegen muss man davon relativ viel essen.
Bild
Antworten