Why are ValF & ValD so slow?

Just starting out? Need help? Post your questions and find answers here.
pjay
Enthusiast
Enthusiast
Posts: 272
Joined: Thu Mar 30, 2006 11:14 am

Re: Why are ValF & ValD so slow?

Post 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.
AZJIO
Addict
Addict
Posts: 2204
Joined: Sun May 14, 2017 1:48 am

Re: Why are ValF & ValD so slow?

Post 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")
pjay
Enthusiast
Enthusiast
Posts: 272
Joined: Thu Mar 30, 2006 11:14 am

Re: Why are ValF & ValD so slow?

Post 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.
AZJIO
Addict
Addict
Posts: 2204
Joined: Sun May 14, 2017 1:48 am

Re: Why are ValF & ValD so slow?

Post by AZJIO »

Code: Select all

Debug _ValD(@"1.1.1") ; 1.11 ?
Debug _ValD(@"1.1.1.1.1") ; 1.1111 ?
pjay
Enthusiast
Enthusiast
Posts: 272
Joined: Thu Mar 30, 2006 11:14 am

Re: Why are ValF & ValD so slow?

Post 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.
User avatar
skywalk
Addict
Addict
Posts: 4236
Joined: Wed Dec 23, 2009 10:14 pm
Location: Boston, MA

Re: Why are ValF & ValD so slow?

Post 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.
The nice thing about standards is there are so many to choose from. ~ Andrew Tanenbaum
AZJIO
Addict
Addict
Posts: 2204
Joined: Sun May 14, 2017 1:48 am

Re: Why are ValF & ValD so slow?

Post 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
Last edited by AZJIO on Sat Oct 04, 2025 2:56 pm, edited 1 time in total.
pjay
Enthusiast
Enthusiast
Posts: 272
Joined: Thu Mar 30, 2006 11:14 am

Re: Why are ValF & ValD so slow?

Post 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...
User avatar
Piero
Addict
Addict
Posts: 1003
Joined: Sat Apr 29, 2023 6:04 pm
Location: Italy

Re: Why are ValF & ValD so slow?

Post 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
User avatar
STARGÅTE
Addict
Addict
Posts: 2245
Joined: Thu Jan 10, 2008 1:30 pm
Location: Germany, Glienicke
Contact:

Re: Why are ValF & ValD so slow?

Post 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.
PB 6.01 ― Win 10, 21H2 ― Ryzen 9 3900X, 32 GB ― NVIDIA GeForce RTX 3080 ― Vivaldi 6.0 ― www.unionbytes.de
Lizard - Script language for symbolic calculations and moreTypeface - Sprite-based font include/module
Fred
Administrator
Administrator
Posts: 18312
Joined: Fri May 17, 2002 4:39 pm
Location: France
Contact:

Re: Why are ValF & ValD so slow?

Post by Fred »

Should be much faster in 6.30b3
pjay
Enthusiast
Enthusiast
Posts: 272
Joined: Thu Mar 30, 2006 11:14 am

Re: Why are ValF & ValD so slow?

Post 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
Fred
Administrator
Administrator
Posts: 18312
Joined: Fri May 17, 2002 4:39 pm
Location: France
Contact:

Re: Why are ValF & ValD so slow?

Post by Fred »

Glad to hear! :)
Post Reply