Page 2 of 2

Re: Why are ValF & ValD so slow?

Posted: Wed Oct 01, 2025 2:38 pm
by pjay
With which version of PB?
I'm using PB versions Win x64 v6.21 & v6.3b2, but the performance has been an issue for years looking at historical posts.

Re: Why are ValF & ValD so slow?

Posted: Wed Oct 01, 2025 2:56 pm
by AZJIO
pjay wrote: Wed Oct 01, 2025 1:25 pm The test code I used is below:
Don't you have a check for letters at the end of a fraction?

Code: Select all

Debug _ValD(@"1.1a")

Re: Why are ValF & ValD so slow?

Posted: Wed Oct 01, 2025 3:20 pm
by pjay
AZJIO wrote: Wed Oct 01, 2025 2:56 pm
pjay wrote: Wed Oct 01, 2025 1:25 pm The test code I used is below:
Don't you have a check for letters at the end of a fraction?

Code: Select all

Debug _ValD(@"1.1a")
No, I checked characters before and after an integer, but not a fractional number - I've added that one now, thanks.

Re: Why are ValF & ValD so slow?

Posted: Wed Oct 01, 2025 3:49 pm
by AZJIO

Code: Select all

Debug _ValD(@"1.1.1") ; 1.11 ?
Debug _ValD(@"1.1.1.1.1") ; 1.1111 ?

Re: Why are ValF & ValD so slow?

Posted: Wed Oct 01, 2025 4:09 pm
by pjay
AZJIO wrote: Wed Oct 01, 2025 3:49 pm

Code: Select all

Debug _ValD(@"1.1.1") ; 1.11 ?
Debug _ValD(@"1.1.1.1.1") ; 1.1111 ?
Lots of edge cases, added, thanks.

Re: Why are ValF & ValD so slow?

Posted: Thu Oct 02, 2025 12:34 am
by skywalk
That's why I suggested atof() or equivalent wtof().

I don't have ValD() in many high speed scenarios but thanks for this post. I will check my code.

Re: Why are ValF & ValD so slow?

Posted: Fri Oct 03, 2025 1:35 am
by AZJIO
I added an exponential format analyzer and increased the speed.

Code: Select all

EnableExplicit
DisableDebugger

#SzChar = SizeOf(Character) ; =2


; an improvement from the STARGÅTE
Procedure.d Pow2(n, replay)
	Protected result.d = 1, bit = 1<<8 ; most significant bit, 256, enough for double exponent
	If replay > 308
		ProcedureReturn Infinity()
	EndIf
	While replay & bit = 0 And bit  ; Searching the most significant bit
		bit >> 1
	Wend
	While bit > 0
		result * result
		If replay & bit
			result * n
		EndIf
		bit >> 1
	Wend
	ProcedureReturn result
EndProcedure

	
Procedure.d ValD2(*c.Character)
	Protected negative, decimal, exponent, negative2 ; , pos
	Protected divider = 1
	Protected result.d
; 	Protected *pLong.long = *c
	
	If *c = 0 Or *c\c = 0
		ProcedureReturn NaN() ; Or 0?
	EndIf
	
	While *c\c = ' ' Or *c\c = '	'
		*c + #SzChar
	Wend
	
; 	If *pLong\l = 6357070 And CompareMemory(*c, @"NaN", 6)
; 		ProcedureReturn NaN()
; 	EndIf
; 	If (*pLong\l = 6357070 And CompareMemory(*c, @"+Infinity", 18)) Or (*pLong\l = 6357070 And CompareMemory(*c, @"Infinity", 16))
; 		ProcedureReturn Infinity()
; 	EndIf
; 	If *pLong\l = 6357070 And CompareMemory(*c, @"-Infinity", 18)
; 		ProcedureReturn -Infinity()
; 	EndIf

	
	If *c\c = '-'
		*c + #SzChar
		negative = 1
	ElseIf *c\c = '+'
		*c + #SzChar
	EndIf
	
	While *c\c = '0'
		*c + #SzChar
	Wend
	
	; get the integer part
	If *c\c > 47 And *c\c < 58 ; to avoid multiplying by 10 and then dividing by 10 unnecessarily.
		result + *c\c - 48
		*c + #SzChar
		While *c\c > 47 And *c\c < 58
; 			pos + 1
			result * 10 + *c\c - 48
			*c + #SzChar
		Wend
; 		If pos > 14
; 			ProcedureReturn NaN()
; 		EndIf
	EndIf
	
	; get the decimal part of a number
	If *c\c = '.'
		*c + #SzChar
		While *c\c > 47 And *c\c < 58
; 			pos + 1
			divider * 10
			decimal * 10 + *c\c - 48
			*c + #SzChar
		Wend
; 		If pos > 14
; 			ProcedureReturn NaN()
; 		EndIf
		result + decimal / divider
	EndIf
	
	; get an exponent
	If *c\c = 'e' Or *c\c = 'E'
		*c + #SzChar
		If *c\c = '-'
			*c + #SzChar
			negative2 = 1
		EndIf
		While *c\c = '0'
			*c + #SzChar
		Wend
		If *c\c > 47 And *c\c < 58 ; to avoid multiplying by 10 and then dividing by 10 unnecessarily.
			exponent + *c\c - 48
			*c + #SzChar
			While *c\c > 47 And *c\c < 58
				exponent * 10 + *c\c - 48
				*c + #SzChar
			Wend
			If negative2
				result / Pow2(10, exponent)
			Else
				result * Pow2(10, exponent)
			EndIf
		EndIf
	EndIf
	
	If negative
		result = -result
	EndIf
	
	ProcedureReturn result
EndProcedure

; Debug ValD2(@"922337203685477") ; max
; Debug ValD2(@"92233720368547.3") ; max (decimal)
; Debug ValD2(0) ; pointer 0
; Debug ValD2(@"") ; empty line
; Debug ValD2(@"NaN") ; special value
; Debug ValD2(@"Infinity")
; Debug ValD2(@"+Infinity")
; Debug ValD2(@"-Infinity")
; Debug ValD2(@"1.2.3") ; Two dots
; Debug ValD2(@"00123") ; Preceding zeros
; Debug ValD2(@"-12 3") ; a negative number
; Debug ValD2(@"567.123") ; a number with a decimal part
; Debug ValD2(@"000123.00456")
; Debug ValD2(@".789") ; a number without a preceding zero
; Debug ValD2(@"789.")
; Debug ValD2(@"-2.923e01") ; exponential notation
; Debug ValD2(@"-2.923E01")
; Debug ValD2(@"300e05")
; Debug ValD2(@"300e-02")

#Counts = 1000000
Define Time, String.s, Double.d, i

Time = ElapsedMilliseconds()
String = "567.123"
For i = 1 To #Counts
	Double = ValD2(@String)
Next
Time = ElapsedMilliseconds()-Time

OpenConsole()
PrintN("Single call time: "+StrD(1000.0*Time/#Counts)+" µs")
Input()
pjay

Code: Select all

	; single digit quicky?
	*scTest = *pString + 2
	If *scTest\c = 0
		If *pString\c > 47 And *pString\c < 58
			ProcedureReturn *pString\c - '0'
		Else
			ProcedureReturn 0.0
		EndIf
	EndIf
This speeds up for digits 0-9, but slows down for other numbers. Since ValD() works with fractional numbers, the 0-9 range does not have a high priority.

Without creating a variable:

Code: Select all

	; single digit quicky?
	If PeekC(*c + #SzChar) = 0
		If *c\c > 47 And *c\c < 58
			ProcedureReturn *c\c - 48
		Else
			ProcedureReturn 0.0
		EndIf
	EndIf
This code is dangerous:

Code: Select all

While *pString\c <> endChar
If endChar is not present in the string, an infinite loop can occur. To avoid this, you should also check that the value in the pointer is not Null

Re: Why are ValF & ValD so slow?

Posted: Fri Oct 03, 2025 12:29 pm
by pjay

Code: Select all

This code is dangerous...
No it isn't - the caller of this function (a data parser) knows precisely what the delimiting character is (null, comma, pipe, space, tab etc.).

This wasn't a 'Tricks 'n' Tips' post...

Re: Why are ValF & ValD so slow?

Posted: Fri Oct 03, 2025 2:28 pm
by Piero
Fred wrote: Wed Oct 01, 2025 1:00 pm We set the locale everytime
It's very interesting to me how PB has to struggle between low and high-level needs (but generally there are many experienced programmers here who can propose faster "low-level" alternatives in no time…)
I stop here because my duty is to criticize you :P

Re: Why are ValF & ValD so slow?

Posted: Sat Oct 04, 2025 12:20 pm
by STARGÅTE
AZJIO wrote: Fri Oct 03, 2025 1:35 am

Code: Select all

Procedure Pow2(n, replay)
	Protected i, result = 1
	While replay > 0
		replay - 1
		result * n
	Wend
	ProcedureReturn result
EndProcedure
You have a bug in the use of your Pow2() function.
A double number can have exponents up to e+308 oder e-308, but the variable result and the return type is just an integer, it must be a double.

Further, for exponents like e300 your loop iterates over 300 times.
It would be more efficient to use the Exponentiation by squaring method.

Code: Select all

Procedure.d Pow2(n, replay)
	Protected result.d = 1, bit = 1<<8 ; most significant bit, 256, enough for double exponent
	If replay > 308
		ProcedureReturn Infinity()
	EndIf
	While replay & bit = 0 And bit  ; Searching the most significant bit
		bit >> 1
	Wend
	While bit > 0
		result * result
		If replay & bit
			result * n
		EndIf
		bit >> 1
	Wend
	ProcedureReturn result
EndProcedure
Searching the most significant bit can be also written in ASM or C for speed up.

Re: Why are ValF & ValD so slow?

Posted: Mon Oct 06, 2025 1:06 pm
by Fred
Should be much faster in 6.30b3

Re: Why are ValF & ValD so slow?

Posted: Mon Oct 06, 2025 2:31 pm
by pjay
Fred wrote: Mon Oct 06, 2025 1:06 pm Should be much faster in 6.30b3
Both ValF & ValD are much quicker for me now Fred, thanks a lot. :D

Re: Why are ValF & ValD so slow?

Posted: Mon Oct 06, 2025 5:31 pm
by Fred
Glad to hear! :)