IsNumeric()

Share your advanced PureBasic knowledge/code with the community.
RTEK
User
User
Posts: 28
Joined: Sat Aug 25, 2007 10:10 pm
Location: USA

IsNumeric()

Post by RTEK »

I needed to use IsNumeric() that returns true or false only so I coded this. Maybe someone would also find this useful. I added comments as best as I can.

This version takes only 1 parameter - input can be integer or float, signed or unsigned, with or without comma-separators.
It returns just 2 values: 1 (true) or 0 (false).

NOTE: Hexadecimal numbers and scientific notations are not taken into account - if they are used as input, the function will return false.

Let me know if you find any valid numbers that is being flagged as not numeric as well as any invalid numbers that is passing as numeric.

I found that there are already several versions of IsNumeric() posted here, but anyways here my spin on it :wink:

Code: Select all

Procedure IsNumeric(String.s) 
	;remove any leading and/or trailing spaces
	string = Trim(string)
	
	;we expect the +/- sign at the beginning of the string only
	
	;remove leading positive sign if present	
	If Left(string,1) = "+"
		string = Right(string, Len(string)-1)
		;extra + or - signs make the string not a valid numeric
		If FindString(string, "+", 1) > 0 Or FindString(string, "-", 1) > 0 
			ProcedureReturn #False
		EndIf
	EndIf
	
	;remove leading negative sign if present
	If Left(string,1) = "-"
		string = Right(string, Len(string)-1)
		;extra + or - signs make the string not a valid numeric
		If FindString(string, "+", 1) > 0 Or FindString(string, "-", 1) > 0 		
			ProcedureReturn #False
		EndIf		
	EndIf
	
	;valid numbers can have only 1 dot for the decimal point if used, so we're checking for that
	dotCt = CountString(String, ".")
	
	;multiple dots in the string make it non-numeric
	If dotCt > 1
		ProcedureReturn #False
	EndIf
	
	;in the absence of a decimal point, we evaluate the remaining string 
	If dotCt = 0
		;check for comma separators
		If FindString(string, ",", 1) > 0
			leftPart.s = string
			;check that we have the right number of commas
			commaCt = CountString(leftPart, ",")
			noCommaString.s = ReplaceString(leftPart, ",", "")
			expectedCommaCt = Len(noCommaString) / 3
			If Len(noCommaString) % 3 = 0
				expectedCommaCt = expectedCommaCt - 1
			EndIf
			If commaCt <> expectedCommaCt
				ProcedureReturn #False	
			EndIf
			
			;Find the position of all commas
			Dim commaPos(commaCt)
			commaPos(0) = FindString(leftPart, ",", 1)	
			For x=1 To commaCt-1
				commaPos(x) = FindString(leftPart, ",", commaPos(x-1)+1)	
				;if there are multiple commas, there should be 3 digits in between succeeding commas
				If commaPos(x) - commaPos(x-1) <> 4
					ProcedureReturn #False						
				EndIf
			Next x
			
			;string have commas in the right positions, now check the chars in the string if they are digits or a comma		
			For x=1 To Len(leftPart)
				If FindString("1234567890,", Mid(leftPart, x, 1), 1) = 0
					ProcedureReturn #False		
				EndIf
			Next x
			;if comma separators were used properly, remove them before any further validations are done
			string = ReplaceString(leftPart, ",", "")
		EndIf

		;if empty string or sign only (+ or -) was passed
		If string = ""
			ProcedureReturn #False					
		EndIf
			
		If string = RSet(Str(Val(string)), Len(string), "0")		
			ProcedureReturn #True
		Else
			ProcedureReturn #False
		EndIf
	EndIf
	
	;if valid decimal point is present, we evaluate the portions before and after the decimal point separately
	If dotCt = 1
		;if there's nothing in the string but the dot; it's not a number
		If Len(string)=1
			ProcedureReturn #False
		EndIf
		
		;split the string into the parts before and after the decimal point
		dotPos = FindString(String, ".", 1)
		leftPart.s = Left(String, dotPos-1)
		rightPart.s = Right(String, Len(String)-dotPos)
		
		;there should be no comma separator in the decimal portion of any valid number
		If FindString(rightPart, ",", 1) > 0
			ProcedureReturn #False
		EndIf

		;check for comma separators
		If FindString(leftPart, ",", 1) > 0		
			;check that we have the right number of commas
			commaCt = CountString(leftPart, ",")
			noCommaString.s = ReplaceString(leftPart, ",", "")
			expectedCommaCt = Len(noCommaString) / 3
			If Len(noCommaString) % 3 = 0
				expectedCommaCt = expectedCommaCt - 1
			EndIf
			If commaCt <> expectedCommaCt
				ProcedureReturn #False	
			EndIf

			;Find the position of all commas
			Dim commaPos(commaCt)			
			commaPos(0) = FindString(leftPart, ",", 1)	
			For x=1 To commaCt-1
				commaPos(x) = FindString(leftPart, ",", commaPos(x-1)+1)	
				;if there are multiple commas, there should be 3 digits in between succeeding commas
				If commaPos(x) - commaPos(x-1) <> 4
					ProcedureReturn #False						
				EndIf
			Next x
			
			;string have commas in the right positions, now check the chars in the string if they are digits or comma		
			For x=1 To Len(leftPart)
				If FindString("1234567890,", Mid(leftPart, x, 1), 1) = 0
					ProcedureReturn #False		
				EndIf
			Next x
			;if comma separators were used properly, remove them before any further validations are done
			leftPart = ReplaceString(leftPart, ",", "")
		EndIf

		If leftPart = RSet(Str(Val(leftPart)), Len(leftPart), "0")
			If rightPart = RSet(Str(Val(rightPart)), Len(rightPart),"0")
				;the parts of the string before and after the decimal point are both numeric
				ProcedureReturn #True
			Else				
				;the part of the string after the decimal point is not numeric
				ProcedureReturn #False
			EndIf
		Else
			If leftPart = "" And Left(string,1) = "."
				If rightPart = RSet(Str(Val(rightPart)), Len(rightPart),"0")
					;the remaining string begins with a decimal point and everything after that is numeric
					ProcedureReturn #True
				EndIf			
			Else
				;the part of the string before the decimal point is not numeric
				ProcedureReturn #False
			EndIf
		EndIf		
	EndIf
EndProcedure
And here are some basic tests I ran

Code: Select all

Debug "UNSIGNED NUMBERS"
Debug IsNumeric("0")
Debug IsNumeric("1")
Debug IsNumeric(".0")
Debug IsNumeric(".1")
Debug IsNumeric("0.0")
Debug IsNumeric("0.1")
Debug IsNumeric("1.0")
Debug IsNumeric("1.1")
Debug IsNumeric("0.")
Debug IsNumeric("1.")

Debug ""
Debug "NEGATIVE SIGNED NUMBERS "
Debug IsNumeric("-0")
Debug IsNumeric("-1")
Debug IsNumeric("-.0")
Debug IsNumeric("-.1")
Debug IsNumeric("-0.0")
Debug IsNumeric("-0.1")
Debug IsNumeric("-1.0")
Debug IsNumeric("-1.1")
Debug IsNumeric("-0.")
Debug IsNumeric("-1.")

Debug ""
Debug "POSITIVE SIGNED NUMBERS "
Debug IsNumeric("+0")
Debug IsNumeric("+1")
Debug IsNumeric("+.0")
Debug IsNumeric("+.1")
Debug IsNumeric("+0.0")
Debug IsNumeric("+0.1")
Debug IsNumeric("+1.0")
Debug IsNumeric("+1.1")
Debug IsNumeric("+0.")
Debug IsNumeric("+1.")

Debug ""
Debug "NUMBERS PADDED WITH ZERO(S)"
Debug IsNumeric("01")
Debug IsNumeric("+01")
Debug IsNumeric("+01.")
Debug IsNumeric("-01")
Debug IsNumeric("-01.")
Debug IsNumeric("+01.0")
Debug IsNumeric("+01.1")
Debug IsNumeric("-01.0")
Debug IsNumeric("-01.1")

Debug ""
Debug "NUMBERS WITH COMMA SEPARATORS"
Debug IsNumeric("1,000")
Debug IsNumeric("10,000")
Debug IsNumeric("100,000")
Debug IsNumeric("1,000,000")
Debug IsNumeric("1,000.00")
Debug IsNumeric("10,000.00")
Debug IsNumeric("100,000.00")
Debug IsNumeric("1,000,000.00")
Debug IsNumeric("-1,000")
Debug IsNumeric("-1,000,000.00")

Debug ""
Debug "INVALID NUMBERS"
Debug IsNumeric(".")
Debug IsNumeric("+.")
Debug IsNumeric("-.")
Debug IsNumeric("1+")
Debug IsNumeric("1-")
Debug IsNumeric("+-1")
Debug IsNumeric("+-1.0")
Debug IsNumeric("1.0.0")
Debug IsNumeric("abc")
Debug IsNumeric("-1.5E-7")
Debug IsNumeric("1.000,123")
Debug IsNumeric("123456789,")
Debug IsNumeric("12345678,9")
Debug IsNumeric("1234567,89")
Debug IsNumeric("123456,789")
Debug IsNumeric("123,456789")
Debug IsNumeric("12,3456,789")
Debug IsNumeric(",123,456,789")
Debug IsNumeric("-")
Debug IsNumeric("+")
Debug IsNumeric("")
Last edited by RTEK on Sat Mar 05, 2011 8:13 pm, edited 1 time in total.
User avatar
skywalk
Addict
Addict
Posts: 4211
Joined: Wed Dec 23, 2009 10:14 pm
Location: Boston, MA

Re: IsNumeric()

Post by skywalk »

Debug IsNumeric("-1.5E-7") = 0, but this is a valid number in scientific notation.

Debug IsNumeric("-.0") = 1, but not recognized by PB
Debug IsNumeric("-.1") = 1, but not recognized by PB

I use my IsNumeric() to verify user input fields going to math functions.
True means I do not have to alter the number prior to passing to my procedures.
The nice thing about standards is there are so many to choose from. ~ Andrew Tanenbaum
RTEK
User
User
Posts: 28
Joined: Sat Aug 25, 2007 10:10 pm
Location: USA

Re: IsNumeric()

Post by RTEK »

Skywalk,
you missed my note regarding hex numbers and scientific notations in my original posting.

what do you mean by "not recognized by PB"?
Debug IsNumeric("-.0") = 1, but not recognized by PB
Debug IsNumeric("-.1") = 1, but not recognized by PB
Image
Last edited by RTEK on Sat Mar 05, 2011 6:55 pm, edited 1 time in total.
User avatar
skywalk
Addict
Addict
Posts: 4211
Joined: Wed Dec 23, 2009 10:14 pm
Location: Boston, MA

Re: IsNumeric()

Post by skywalk »

RTEK wrote:Skywalk,
you missed my note regarding hex numbers and scientific notations in my original posting.

what do you mean by "not recognized by PB"?
Debug IsNumeric("-.0") = 1, but not recognized by PB
Debug IsNumeric("-.1") = 1, but not recognized by PB
Yes, I saw your preface, but wanted to explain why I use IsNumeric().
If you make it only report 1 or 0, then you limit its usefulness.
Since it is an integer, add other values, like 2 = floating point, etc.

This confuses me a bit, so I flag .1 or -.1 as not numbers, because of the PB syntax error.

Code: Select all

Debug ValD("-.1 * 1") ;<--- Why this works?
                      ;     But not
Debug -.1 * 1         ;<--- Syntax Error
The nice thing about standards is there are so many to choose from. ~ Andrew Tanenbaum
RTEK
User
User
Posts: 28
Joined: Sat Aug 25, 2007 10:10 pm
Location: USA

Re: IsNumeric()

Post by RTEK »

I don't have an answer for the PB syntax error you pointed out.
skywalk wrote: True means I do not have to alter the number prior to passing to my procedures.
IsNumeric() takes a string as input so you still have to use Val(), ValF() or ValD() to use that string in computations.

Using ValF() or ValD() on the string, makes "-.0" and "-.1" valid numerics as shown in the earlier image I posted.

In both our versions, our IsNumeric() accepts comma-separators, but to use strings that have commas we still need to remove the comma(s)

Image
User avatar
skywalk
Addict
Addict
Posts: 4211
Joined: Wed Dec 23, 2009 10:14 pm
Location: Boston, MA

Re: IsNumeric()

Post by skywalk »

Yes, your point is taken on the requirement to use Val(somestring$) to do actual math.
I just always defer to the syntax error for now.

I have user fields that allow multiple entries separated by commas, so I don't allow commas in numbers.
This is just my specifics.

Thanks for the posting though.
You have caused me to ponder why PB doesn't handle the expression below as it does with Val()?

Code: Select all

Debug ValD("-.1 * 1") ;<--- Why this works?
                      ;     But not
Debug -.1 * 1         ;<--- Syntax Error
RTEK
User
User
Posts: 28
Joined: Sat Aug 25, 2007 10:10 pm
Location: USA

Re: IsNumeric()

Post by RTEK »

Original post updated to check for empty string or sign only (+/-) passed to IsNumeric

Code: Select all

		;if empty string or sign only (+ or -) was passed
		If string = ""
			ProcedureReturn #False					
		EndIf
and corresponding test

Code: Select all

Debug IsNumeric("-")
Debug IsNumeric("+")
Debug IsNumeric("")
User avatar
Frarth
Enthusiast
Enthusiast
Posts: 241
Joined: Tue Jul 21, 2009 11:11 am
Location: On the planet
Contact:

Re: IsNumeric()

Post by Frarth »

skywalk wrote:This confuses me a bit, so I flag .1 or -.1 as not numbers, because of the PB syntax error.
You do not have to force a user input to follow PB's syntax rules. In the procedure just place a zero in front:

Code: Select all

a.s = ".1"

If Left(a, 1) = Chr(46)
  a = Chr(48) + a
EndIf

Debug a ;valid number
PureBasic 5.41 LTS | Xubuntu 16.04 (x32) | Windows 7 (x64)
User avatar
Frarth
Enthusiast
Enthusiast
Posts: 241
Joined: Tue Jul 21, 2009 11:11 am
Location: On the planet
Contact:

Re: IsNumeric()

Post by Frarth »

@RTEK: Personally I would check for plus and minus sign in one block:

Code: Select all

Select Left(string, 1)
  Case "+", "-"
    string = Mid(string, 2)
    If FindString(string, "+", 1) Or FindString(string, "-", 1)
      ProcedureReturn #False
    EndIf
EndSelect
PureBasic 5.41 LTS | Xubuntu 16.04 (x32) | Windows 7 (x64)
Post Reply