Schnellere Alternative zu StrD()?

Für allgemeine Fragen zur Programmierung mit PureBasic.
Benutzeravatar
STARGÅTE
Kommando SG1
Beiträge: 6999
Registriert: 01.11.2005 13:34
Wohnort: Glienicke
Kontaktdaten:

Re: Schnellere Alternative zu StrD()?

Beitrag von STARGÅTE »

Tolle Idee Nic, hätte den versuch eigentlich gar nicht erst probiert, StrD "nachzuschreiben", zumindest nicht für 10er-basen.
Das das nun auch noch vier mal schneller sein soll, krass.
Ich hatte mir eine Prozedur für beliebige Basen geschrieben, wobei ich immer ganz gerne die Exponential-Schreibweise bevorzuge, damit ganz kleine oder ganz große Zahlen nicht so viele unnötige Nullen haben.
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: 8679
Registriert: 29.08.2004 20:20
Computerausstattung: Ryzen 7 5800X, 32 GB DDR4-3200
Ubuntu 22.04.3 LTS
GeForce RTX 3080 Ti
Wohnort: Saarbrücken
Kontaktdaten:

Re: Schnellere Alternative zu StrD()?

Beitrag von NicTheQuick »

Ich war selbst erstaunt, dass es schneller geht. :lol:
Ich dachte eigentlich die Arbeit sei für die Katz. Aber siehe da. 8)

Die Exponential-Schreibweise habe ich bewusst ignoriert, weil DePe das nicht brauchte. Es passieren auch ein paar verrückte Dinge mit sehr großen Zahlen. Da bin ich mir nicht sicher, ob die Funktion da immer sauber arbeitet. Aber in den Nachkommastellen sollte nichts falsch sein. Und wie sich herausstellt, ist es da sogar etwas genauer. Ziemlich verrückt irgendwie.
Bild
Benutzeravatar
NicTheQuick
Ein Admin
Beiträge: 8679
Registriert: 29.08.2004 20:20
Computerausstattung: Ryzen 7 5800X, 32 GB DDR4-3200
Ubuntu 22.04.3 LTS
GeForce RTX 3080 Ti
Wohnort: Saarbrücken
Kontaktdaten:

Re: Schnellere Alternative zu StrD()?

Beitrag von NicTheQuick »

Ich habe noch einen hässlichen Bug gefunden. Ich habe den Code im letzten Post korrigiert.

Ich habe sogar gerade noch einen Bug gefunden. Man kann nämlich nicht 0 Nachkommastellen einstellen. :lol:
Den fix ich später. Hab grad keine Zeit.
Bild
Benutzeravatar
NicTheQuick
Ein Admin
Beiträge: 8679
Registriert: 29.08.2004 20:20
Computerausstattung: Ryzen 7 5800X, 32 GB DDR4-3200
Ubuntu 22.04.3 LTS
GeForce RTX 3080 Ti
Wohnort: Saarbrücken
Kontaktdaten:

Re: Schnellere Alternative zu StrD()?

Beitrag von NicTheQuick »

Ich habe mir noch ein Fuzzy Testing dazu gebaut, damit ich prüfen kann, ob meine Werte denen von StrD entsprechen oder im Rahmen der Möglichkeiten von Doubles sind. Dabei habe ich noch ein paar Fehlerchen gefunden und beseitigt. Viel Spaß.

Code: Alles auswählen

Threaded StrD2_Buffer.s = "                                                                                                    "
Procedure.s StrD2(value.d, nbDecimals.i = 10)
	Protected *c.Character = @StrD2_Buffer + SizeOf(Character) * 50
	Protected.Character *c1 = *c, *c2 = *c, *e = *c - SizeOf(Character)
	Protected d.i, neg.i = Bool(value < 0.0)
	value = Abs(value)
	Protected.d int2, int = Round(value, #PB_Round_Down)
	value - int
	
	; First the part before the digit
	If int > 0.0
		While int >= 1.0
			int2 = Round(int / 10, #PB_Round_Down)
			*c1 - SizeOf(Character)
			*c1\c = '0' + (10 + Int(int - 10.0 * int2) % 10) % 10
			int = int2
		Wend
	Else
		*c1 - SizeOf(Character)
		*c1\c = '0'
	EndIf
	*c1 - SizeOf(Character)
	*c1\c = ' '
	*c1 + SizeOf(Character)
	
	If nbDecimals > 0
		value * 10.0
		
		; Now for the fractional part
		For d = 2 To nbDecimals
			int = Round(value, #PB_Round_Down)
			*c2 + SizeOf(Character)
			*c2\c = '0' + Int(int)
			If *c2\c > '0'
				*e = *c2
			EndIf
			value - int
			value * 10.0
		Next
		int = Round(value, #PB_Round_Nearest)
	Else
		int = Round(value, #PB_Round_Nearest) * 10
	EndIf
	
	; Now the complicated rounding part
	If int < 10.0 ; no carry after rounding
		*c2 + SizeOf(Character)
		*c2\c = '0' + Int(int)
		If *c2\c > '0'
			*e = *c2
		EndIf
	
	Else ;carry
		; set every 9 at the end to 0
		While *c2\c = '9' And *c2 > *c
			*c2\c = '0'
			*c2 - SizeOf(Character)
		Wend
		; increase the non-9 digit by 1
		If *c2 > *c ; if we are still in the fractional part
			*c2\c + 1
			*e = *c2
		
		Else ; if we need to round up the integer part
			*e = *c - SizeOf(Character)
			*c2 = *e
			; again turn the 9 to a 0
			While *c2\c = '9'
				*c2\c = '0'
				*c2 - SizeOf(Character)
			Wend
			; and increase the last remaining non-9 digit by 1
			If *c2\c = ' '
				*c2\c = '1'
				*c1 = *c2
			Else
				*c2\c + 1
			EndIf
		EndIf
	EndIf
	
	; Add a sign if necessary
	If neg
		*c1 - SizeOf(Character)
		*c1\c = '-'
	EndIf
	*c\c = '.'
	
	ProcedureReturn PeekS(*c1, 1 + (*e - *c1) / SizeOf(Character))
EndProcedure

; Prüfstation
Debug ""
Debug "==============================="
Debug "Test certain valued explicitly:"
Define d.i = 16
Define r.d = ValD("0.9847589999999999399449280")

Debug "  12345678901234567890"
Debug r
Debug StrD(r, d)
Debug StrD2(r, d)
;End

; Examples
Debug ""
Debug "==============================="
Debug "Some Examples:"
Debug StrD2(-0.0099890007, 2)
Debug StrD2(-123.4569, 3)
Debug StrD2(9.999, 2)
Debug StrD2(79.999, 2)
Debug StrD2(13.539327, 0)
Debug "10^23 with StrD2: " + StrD2(Pow(11,23), 14)
Debug "10^23 with StrD:  " + StrD(Pow(11,23), 14)

; Test


; change to 2 to actually see the different outcomes

; Mode of operation
; 0: Just made statistics over the defined number of rounds
; 1: The same as 0
; 2: Ignore #ROUNDS and loop until the difference in the last digit is bigger than 0.5 (should be endless)
; 3: Ignore #ROUNDS and loop until the general output differs (can result in differences)
#MODE = 1

; Number of rounds for statistics if #MODE < 2
#ROUNDS = 60000

; Make debuglevel dependent of #MODE
DebugLevel #MODE

Define wins1.i = 0, wins2.i = 0, ties.i = 0
Define var1.d, var2.d, varSum1.d, varSum2.d
Define i.i, s1.s, s2.s, r.d, d.i, r1.d, r2.d
Define realRounds.i

Debug ""
Debug "==============================="
Debug "Looping..."

For i = 1 To #ROUNDS

	realRounds + 1
	; It makes no sense to test for 16+ digits because the mantisse would need more than 52 bits for that
	d = Random(15)
	r = Random(100000000) * 0.000001
	
	s1 = StrD(r, d)
	If FindString(s1, ".")
		s1 = RTrim(RTrim(s1, "0"), ".")
	EndIf
	
	s2 = StrD2(r, d)
	
	If s1 <> s2
		Debug "", 3
		Debug "    r = " + StrD(r, 25), 3
		Debug "    d = " + d, 3
		Debug "StrD  = " + s1, 3
		Debug "StrD2 = " + s2, 3
		r1.d = ValD(s1)
		r2.d = ValD(s2)
		var1 = Abs(r1 - r) * Pow(10, d - 1)
		var2 = Abs(r2 - r) * Pow(10, d - 1)
		Debug StrD(var1, 10) + " | " + StrD(var2, 10), 3
		If var2 - var1 > 0.5
			wins1 + 1
			Debug "StrD  wins! (" + var1 + " < " + var2 + ") => " + StrD(r, 25) + " | " + d, 2
			Debug "StrD  = " + s1, 2
			Debug "StrD2 = " + s2, 2
			If #MODE = 2
				Break
			EndIf
		ElseIf var1 - var2 > 0.5
			wins2 + 1
			Debug "StrD2 wins! (" + var1 + " > " + var2 + ") => " + StrD(r, 25) + " | " + d, 2
			Debug "StrD  = " + s1, 2
			Debug "StrD2 = " + s2, 2
			If #MODE = 2
				Break
			EndIf
		Else
			ties + 1
		EndIf
		If #MODE = 3
			Break
		EndIf
	EndIf
	If #MODE > 1
		i = 0
	EndIf
Next

Debug ""
Debug "==============================="
Debug "ROUNDS = " + realRounds
Debug "Differences: " + Str(wins1 + wins2 + ties)
Debug "Percentage: " + StrD((wins1 + wins2 + ties) * 100.0 / realRounds, 4) + "%"
Debug "StrD  Wins: " + wins1
Debug "StrD2 Wins: " + wins2
Debug "      Ties: " + ties
Bild
DePe
Beiträge: 153
Registriert: 26.11.2017 16:17
Wohnort: Wien
Kontaktdaten:

Re: Schnellere Alternative zu StrD()?

Beitrag von DePe »

Hallo NicTheQuick,

danke nochmals.
Ich habe deine Prozedur in mein Programm übernommen, und ansonst nichts weiter optimiert. Das Speichern von 56 MB Messdaten dauert mit PB StrD() 11:08 Minuten, mit deiner Version nur 6:40 Minuten. Die CSV-Datei hat 1,6 GB. Im Vergleich die Binär-Datei wird in 18 Sekunden gespeichert und hat 1 GB.
Ich habe auch einen Verweis auf dich und diesen Beitrag, in den About-Dialog aufgenommen. Das mache ich mit jedem Code aus dem Forum.

Peter
Benutzeravatar
NicTheQuick
Ein Admin
Beiträge: 8679
Registriert: 29.08.2004 20:20
Computerausstattung: Ryzen 7 5800X, 32 GB DDR4-3200
Ubuntu 22.04.3 LTS
GeForce RTX 3080 Ti
Wohnort: Saarbrücken
Kontaktdaten:

Re: Schnellere Alternative zu StrD()?

Beitrag von NicTheQuick »

Cool, freut mich. :allright:
Tja, sieht so aus als ist es nicht ganz so einfach Zahlen zu konvertieren.

Wenn du jetzt überlegst, dass das Programm, dass die CSV einliest, die Zahlen wieder zurück konvertieren muss, ist das echt unpraktisch. :lol:
Bild
Antworten