Fast functions for numeric string value test
Posted: Sun Dec 31, 2023 1:06 pm
Inspired by @highend's ongoing quest for the fastest possible extraction of trailing numbers from a string, https://www.purebasic.fr/english/viewto ... 54#p613654 I thought I'd use the opportunity to develop the code into numeric test functions. I seem to recall the question has been raised in the past but nevertheless I needed to write them myself, using only pointers.
Code: Select all
EnableExplicit
; **
; ** Numeric string value test routines, 3 forms
; ** ===========================================
; **
; ** (1) Strict test IsNumberStrict()
; ** (2) Integer only test IsNumberInt()
; ** (3) Other test IsNumber() For reasons of need for compatibility with another environment, this
; ** permits an input of only a '.' or '-' to be considered as numbers.
; **
; **
; **
; ** (1) Is number [strict check], returns true or false from string pointer
; ** Disallows only - or . and requires that - and . must be accompanied by at least a numeric digit
; **
Procedure.b IsNumberStrict(*StrPtr.Character)
Protected num.b = #True ; Number flag, assume true until otherwise
Protected dec.b ; Decimal point counter
Protected numfound.b ; Number subsequent to neg. sign found
If *StrPtr\c = 45 ; Neg. sign, if used must always be first char.
*StrPtr + 2
EndIf
While *StrPtr\c ; Loop through until trailing zero
Select *StrPtr\c ; Check non-zero
Case 48 To 57 ; Numeric range 0 to 9
*StrPtr + 2 ; Advance byte pos.
numfound.b = #True ; Number found
Case 46 ; Decimal point
If dec.b ; Already seen decimal point, so
num.b = #False ; ... declare this as non-numeric
Break
EndIf
*StrPtr + 2 ; Advance byte pos.
dec.b = #True ; Set flag, decimal found
Default
num.b = #False ; Non-numeric
Break
EndSelect
Wend
If numfound.b
ProcedureReturn num.b ; Return flag, true = number
EndIf
EndProcedure
; **
; ** (2) Is number check [integer only, non-strict], returns true or false from string pointer
; **
Procedure.b IsNumberInt(*StrPtr.Character)
Protected num.b = #True ; Number flag, assume true until otherwise
Protected numfound.b ; Number subsequent to neg. sign found
If *StrPtr\c = 45 ; Neg. sign, if used must always be first char.
*StrPtr + 2
EndIf
While *StrPtr\c ; Loop through until trailing zero
Select *StrPtr\c ; Check non-zero
Case 48 To 57 ; Numeric range 0 to 9
*StrPtr + 2 ; Advance byte pos.
numfound.b = #True ; Number found
Default
num.b = #False ; Non-numeric
Break
EndSelect
Wend
If numfound.b
ProcedureReturn num.b ; Return flag, true = number
EndIf
EndProcedure
; **
; ** (3) Is number check [non-strict], returns true or false from string pointer, allows a string consisting of just - or . for
; ** reasons of compatibility
; **
Procedure.b IsNumber(*StrPtr.Character)
Protected num.b = #True ; Number flag, assume true until otherwise
Protected dec.b ; Decimal point counter
If *StrPtr\c = 45 ; Neg. sign, if used must always be first char.
*StrPtr + 2
EndIf
While *StrPtr\c ; Loop through until trailing zero
Select *StrPtr\c ; Check non-zero
Case 48 To 57 ; Numeric range 0 to 9
*StrPtr + 2 ; Advance byte pos.
Case 46 ; Decimal point
If dec.b ; Already seen decimal point, so
num.b = #False ; ... declare this as non-numeric
Break
EndIf
*StrPtr + 2 ; Advance byte pos.
dec.b = #True ; Set flag, decimal found
Default
num.b = #False ; Non-numeric
Break
EndSelect
Wend
ProcedureReturn num.b ; Return flag, true = number
EndProcedure
OpenConsole()
Define ans.s
Repeat
Print("Please enter a test value : ")
ans.s = Input()
If IsNumberStrict(@ans.s) ; Use the functions by passing string by reference, this way
PrintN("Yes, that is a valid number")
Else
PrintN("No, that is not a valid number")
EndIf
ForEver