IsNumber

Share your advanced PureBasic knowledge/code with the community.
Dräc
Enthusiast
Enthusiast
Posts: 150
Joined: Sat Oct 09, 2004 12:10 am
Location: Toulouse (France)
Contact:

Post by Dräc »

...so my solution:

Code: Select all

Procedure IsNumeric(String.s)
  ProcedureReturn String = RSet(Str(Val(String)), Len(String), "0") 
EndProcedure 

Debug IsNumeric("99") 
Debug IsNumeric("ABC") 
Debug IsNumeric("AB99CD") 
Debug IsNumeric("0") 
Debug IsNumeric("0000899") 
Debug IsNumeric("0A00899") 
Dräc
AND51
Addict
Addict
Posts: 1040
Joined: Sun Oct 15, 2006 8:56 pm
Location: Germany
Contact:

Post by AND51 »

Dräc's Code optimized for QUADS:

Code: Select all

Procedure IsNumeric(String.s) 
  ProcedureReturn String = RSet(StrQ(ValQ(String)), Len(String), "0") 
EndProcedure 

Debug IsNumeric("99") 
Debug IsNumeric("ABC") 
Debug IsNumeric("AB99CD") 
Debug IsNumeric("0") 
Debug IsNumeric("0000899") 
Debug IsNumeric("0A00899") 
:wink:
PB 4.30

Code: Select all

onErrorGoto(?Fred)
Xombie
Addict
Addict
Posts: 898
Joined: Thu Jul 01, 2004 2:51 am
Location: Tacoma, WA
Contact:

Post by Xombie »

I dug out my slightly older non-ASM solution that handles several different cases. Well, it should, anyway...

Code: Select all

Macro _HandleNumeric()
   ;
   If CaughtSpace = #True : ProcedureReturn #False : EndIf
   ;
   If CaughtDecimal = #True
      ;
      CountDecimal + 1
      ;
   Else
      ;
      If CaughtThousand = #True
         ;
         If CountNumeric > 3 : ProcedureReturn #False : EndIf
         ;
      EndIf
      ;
      CountNumeric + 1
      ;
   EndIf
   ;
   HoldIndex + 1
   ;
EndMacro
Procedure.l IsNumber(InString.s, DecimalCharacter.c = '.', ThousandsSeparator.c = ',') 
   ;
   Define.b CaughtSpace
   ;
   Define.l CaughtDecimal
   ;
   Define.l CountNumeric
   ;
   Define.l CountDecimal
   ;
   Define.l CountNegative
   ;
   Define.l CountPositive
   ;
   Define.b CaughtThousand
   ;
   Define.b CaughtE
   ;
   Define.b IsDecimal
   ;
   Define.b IsNegative
   ;
   Define.l HoldIndex
   ;
   Protected *HoldChar.Character = @InString
   ;
   While *HoldChar\c
      ;
      ; Debug Chr(*HoldChar\c)+" : "+Str(CaughtSpace)
      ;
      If *HoldChar\c = '1'
         ;
         _HandleNumeric()
         ;
      ElseIf *HoldChar\c = '2'
         ;
         _HandleNumeric()
         ;
      ElseIf *HoldChar\c = '3'
         ;
         _HandleNumeric()
         ;
      ElseIf *HoldChar\c = '4'
         ;
         _HandleNumeric()
         ;
      ElseIf *HoldChar\c = '5'
         ;
         _HandleNumeric()
         ;
      ElseIf *HoldChar\c = '6'
         ;
         _HandleNumeric()
         ;
      ElseIf *HoldChar\c = '7'
         ;
         _HandleNumeric()
         ;
      ElseIf *HoldChar\c = '8'
         ;
         _HandleNumeric()
         ;
      ElseIf *HoldChar\c = '9'
         ;
         _HandleNumeric()
         ;
      ElseIf *HoldChar\c = '0'
         ;
         _HandleNumeric()
         ;
      ElseIf *HoldChar\c = ThousandsSeparator
         ;
         If CaughtSpace = #True : ProcedureReturn #False : EndIf
         ;
         If CaughtE = #True : ProcedureReturn #False : EndIf
         ;
         If CaughtDecimal = #True : ProcedureReturn #False : EndIf
         ;
         If CaughtThousand = #True
            ;
            If CountNumeric <> 3 : ProcedureReturn #False : EndIf
            ;
         EndIf 
         ;
         If CountNumeric > 3 : ProcedureReturn #False : EndIf
         ;
         If CountNumeric = 0 : ProcedureReturn #False : EndIf
         ;
         CaughtThousand = #True
         ;
         CountNumeric = 0
         ;
         HoldIndex + 1
         ;
      ElseIf *HoldChar\c = DecimalCharacter
         ;
         If CaughtSpace = #True : ProcedureReturn #False : EndIf
         ;
         If CaughtDecimal = #True : ProcedureReturn #False : EndIf
         ;
         If CountNumeric = 0 : ProcedureReturn #False : EndIf
         ;
         If CaughtE = #True : ProcedureReturn #False : EndIf
         ;
         CaughtDecimal = #True
         ;
         IsDecimal = #True
         ;
         HoldIndex + 1
         ;
      ElseIf *HoldChar\c = ' '
         ;
         If CaughtDecimal = #True
            ;
            CaughtSpace = #True
            ;
         ElseIf CaughtThousand = #True
            ;
            CaughtSpace = #True
            ;
         ElseIf CaughtE = #True
            ;
            CaughtSpace = #True
            ;
         ElseIf CountNumeric = #True
            ;
            CaughtSpace = #True
            ;
         ElseIf CountDecimal = #True
            ;
            CaughtSpace = #True
            ;
         EndIf
         ;
      ElseIf *HoldChar\c = '-' 
         ;
         If CaughtE = #True
            ;
            If CountNegative = 2 : ProcedureReturn #False : EndIf
            ;
            CountNegative + 1
            ;
         Else
            ;
            If HoldIndex
               ;
               ProcedureReturn #False
               ;
            Else
               ;
               CountNegative = 1
               ;
               IsNegative = #True
               ;
            EndIf
            ;
         EndIf
         ;
         HoldIndex + 1
         ;
      ElseIf *HoldChar\c = '+'
         ;
         If CaughtE = #True
            ;
            If CountNegative = 2 : ProcedureReturn #False : EndIf
            ;
            CountPositive = 2
            ;
         Else
            ;
            If HoldIndex : ProcedureReturn #False : EndIf
            ;
            CountPositive = 1
            ;
         EndIf
         ;
         HoldIndex + 1
         ;
      ElseIf *HoldChar\c = 'E'
         ;
         If CaughtE = #True : ProcedureReturn #False : EndIf
         ;
         If CountNumeric = 0 : ProcedureReturn #False : EndIf
         ;
         If CaughtDecimal = #True
            ;
            If CountDecimal = 0 : ProcedureReturn #False : EndIf
            ;
         EndIf
         ;
         CaughtE = #True
         ;
         CountNumeric = 0
         ;
         CountDecimal = 0
         ;
         CaughtDecimal = #False
         ;
         HoldIndex + 1
         ;
      ElseIf *HoldChar\c = 'e'
         ;
         If CaughtE = #True : ProcedureReturn #False : EndIf
         ;
         If CountNumeric = 0 : ProcedureReturn #False : EndIf
         ;
         If CaughtDecimal = #True
            ;
            If CountDecimal = 0 : ProcedureReturn #False : EndIf
            ;
         EndIf
         ;
         CaughtE = #True
         ;
         CountNumeric = 0
         ;
         CountDecimal = 0
         ;
         CaughtDecimal = #False
         ;
         HoldIndex + 1
         ;
      Else
         ;
         ProcedureReturn #False
         ;
      EndIf
      ;
      *HoldChar = *HoldChar + (1 << #PB_Compiler_Unicode)
      ;
   Wend
   ;
   If CountNumeric = 0 : ProcedureReturn #False : EndIf
   ;
   If CaughtThousand
      ;
      If CountNumeric <> 3 : ProcedureReturn #False : EndIf
      ;
   EndIf
   ;
   If IsDecimal
      ;
      If IsNegative : ProcedureReturn -2 : Else : ProcedureReturn 2 : EndIf
      ;
   EndIf
   ;
   If IsNegative : ProcedureReturn -1 : Else : ProcedureReturn 1 : EndIf
   ;
EndProcedure
And some tests...

Code: Select all

Debug "1.  "+Str(IsNumber("+12345.6789"))
; 2
Debug "2.  "+Str(IsNumber("-12345.6789"))
; -2
Debug "3.  "+Str(IsNumber("12345.6789+"))
; 0
Debug "4.  "+Str(IsNumber("123.45.6789+"))
; 0
Debug "5.  "+Str(IsNumber("123456789E-2"))
; 1
Debug "6.  "+Str(IsNumber("+123456789"))
; 1
Debug "7.  "+Str(IsNumber("-123456789"))
; -1
Debug "8.  "+Str(IsNumber("123456789+"))
; 0
Debug "9.  "+Str(IsNumber("e"))
; 0
Debug "10.  "+Str(IsNumber("e "))
; 0 
Debug "11.  "+Str(IsNumber("-e"))
; 0
Debug "12.  "+Str(IsNumber("+e"))
; 0
Debug "13.  "+Str(IsNumber("-1-e"))
; 0
Debug "14.  "+Str(IsNumber("-1E-1"))
; -1
Debug "15.  "+Str(IsNumber("+2E-2"))
; 1
Debug "16.  "+Str(IsNumber("+3183.31151E+321"))
; 2
Debug "17.  "+Str(IsNumber("+3,183.31151E+321"))
; 2
Debug "18.  "+Str(IsNumber("+3183.31151E+90,321"))
; 0
Debug "19.  "+Str(IsNumber("+3183.31151E+321.32"))
; 0
Debug "20.  "+Str(IsNumber("+3183.31151E+3,321.18311"))
; 0
Debug "21.  "+Str(IsNumber("3E2"))
; 1
Debug "22.  "+Str(IsNumber("3e2"))
; 1
Debug "23.  "+Str(IsNumber("-1E+1"))
; -1
Debug "24.  "+Str(IsNumber("+2E+2"))
; 1
Debug "25.  "+Str(IsNumber("+2E"))
; 0
Debug "26.  "+Str(IsNumber("+2E+"))
; 0
Debug "27.  "+Str(IsNumber("+2E-"))
; 0
It should return 0 for non-numeric values, 1 for positive integers, -1 for negative integers, 2 for positive floats and -2 for negative floats.

It should be unicode compatible and handle scientific notation as well as a thousands separator (defaulting to ',' - sorry Europe! :D ) and decimals.

It's fairly quick - a little bit faster than deeem's ASM code. However, I will probably scan through it to check for more optimizations.

..: Edit :.. And it should also ignore leading and trailing spaces.

..: Edit :.. Well, now it does and it's slowed down a little bit to the same speed as deeem's code. However, getting the leading/trailing spaces to be ignored involved a hack so I will definitely need to take a look at this again. This would all be much easier and faster if I didn't need to include scientific notation. What a pain :?
jamba
Enthusiast
Enthusiast
Posts: 144
Joined: Fri Jan 15, 2010 2:03 pm
Location: Triad, NC
Contact:

Re: IsNumber

Post by jamba »

I've been reading over this thread, bringing it back from the dead.

This is a function I use pretty regularly, and For now I am using this from Drac/AND51's code:

Code: Select all

Procedure.i IsNumeric(String.s)
  ;tests whether a string value is numeric or no.
  ; URL: http://www.purebasic.fr/english/viewtopic.php?f=12&t=16668&hilit=isnumeric&start=30
  String = Trim(String) ;remove whitespace
  ProcedureReturn String = RSet(Str(Val(String)), Len(String), "0")
EndProcedure

Debug IsNumeric("  99")
Debug IsNumeric("99  ")
Debug IsNumeric("99")
Debug IsNumeric("ABC")
Debug IsNumeric("AB99CD")
Debug IsNumeric("0")
Debug IsNumeric("0000899")
Debug IsNumeric("0A00899") 
Debug IsNumeric("1.64e-6")    ;false!
Debug IsNumeric("3,000")       ;false!
And it works great!

However, Xombie's code looked interesting being that it could also handle scientific notation (and commas). I do not see these very often, but I guess the more robust the procedure, the better.
Does anyone have anything that can do this, or an updated version?

If not, I'll work on it when I get a minute :)
-Jon

Fedora user
But I work with Win7
Xombie
Addict
Addict
Posts: 898
Joined: Thu Jul 01, 2004 2:51 am
Location: Tacoma, WA
Contact:

Re: IsNumber

Post by Xombie »

Is there something wrong with mine or are you just checking to see if there are other solutions out there?
jamba
Enthusiast
Enthusiast
Posts: 144
Joined: Fri Jan 15, 2010 2:03 pm
Location: Triad, NC
Contact:

Re: IsNumber

Post by jamba »

just other solutions, and also just because you said
However, getting the leading/trailing spaces to be ignored involved a hack so I will definitely need to take a look at this again
didn't know if you have updated it since then, that's all :)

seems to work fine to me.
-Jon

Fedora user
But I work with Win7
User avatar
Kaeru Gaman
Addict
Addict
Posts: 4826
Joined: Sun Mar 19, 2006 1:57 pm
Location: Germany

Re: IsNumber

Post by Kaeru Gaman »

I don't understand why and how

Code: Select all

ProcedureReturn String = RSet(Str(Val(String)), Len(String), "0")
returns that boolean result, and I wonder if it's legal.
oh... and have a nice day.
jamba
Enthusiast
Enthusiast
Posts: 144
Joined: Fri Jan 15, 2010 2:03 pm
Location: Triad, NC
Contact:

Re: IsNumber

Post by jamba »

that is a good point...

maybe this is better/legal?:

Code: Select all

Procedure.i IsNumeric(String.s)
  ;tests whether a string value is numeric or no.
  ; URL: http://www.purebasic.fr/english/viewtopic.php?f=12&t=16668&hilit=isnumeric&start=30
  String = Trim(String) ;remove whitespace
  if String = RSet(Str(Val(String)), Len(String), "0")
    ProcedureReturn #TRUE
  else
    ProcedureReturn 0
  endif
EndProcedure
-Jon

Fedora user
But I work with Win7
User avatar
skywalk
Addict
Addict
Posts: 4211
Joined: Wed Dec 23, 2009 10:14 pm
Location: Boston, MA

Re: IsNumber

Post by skywalk »

Hi,
@Xombie
I don't accept formatted numbers as numeric, but do need to verify Scientific Notation.
Here's a go at IsNumeric() procedure...

Code: Select all

Procedure.i IsNumeric(S.s)
  ; Check only for true numeric quantities.
  ; Formatted numbers using thousands separators or currency marks are 
  ; treated as text and result in #false. 
  ; Zero is the more complicated case.
  Protected.d x
  x = ValD(Trim(S)) 
  If (x > 0 Or x < 0)
    ProcedureReturn #True
  Else  ; Check for a real "0" case.
        ; not a string converted to 0
    S = Trim(LCase(S))
    If Len(s)=0 Or (Len(S)=1 And Asc(S)<>'0')
      ProcedureReturn #False
    Else
      If FindString(S,"-",1) = 1 Or FindString(S,"+",1) = 1
        S = Mid(S,2)      
      EndIf
      S = Trim(S,"0")
      If FindString(S,".",1) = 1 
        S = Mid(S,2)  
        S = Trim(S,"0")  
        If FindString(S,".",1)
          ProcedureReturn #False
        EndIf          
      EndIf
      If FindString(S,"e",1)
        If Len(S)=1      
          ProcedureReturn #True
        EndIf
      EndIf
      If FindString(S,"e+",1) Or FindString(S,"e-",1)
        If Len(S)=2
          ProcedureReturn #True   
        EndIf
      EndIf
      If Len(S)
        ProcedureReturn #False
      Else
        ProcedureReturn #True
      EndIf
    EndIf
  EndIf
EndProcedure

Procedure.i isnumericRE(s.s)
  ; Or try using Regular Expressions...
  ; Can't get scientific notation to work :(
  Protected.s RE
  RE = "^(([0-9+-.$]{1})|([+-]?[$]?[0-9]*(([.]{1}[0-9]*)|([.]?[0-9]+))))$"
  ;RE = "[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?"
  If CreateRegularExpression(0, RE)
    If MatchRegularExpression(0, Trim(s))
      ProcedureReturn #True
    Else
      ProcedureReturn #False
    EndIf
  Else
    Debug RegularExpressionError()
  EndIf 
EndProcedure

Debug "S => IsNumeric(S) => ValD(S)"
Debug "  " + " = > " + Str(IsNumeric("  ")) + " = > " + StrD(ValD("  ")) 
Debug "0" + " = > " + Str(IsNumeric("0")) + " = > " + StrD(ValD("0")) 
Debug "0.0" + " = > " + Str(IsNumeric("0.0")) + " = > " + StrD(ValD("0.0")) 
Debug "+0.0e0" + " = > " + Str(IsNumeric("+0.0e0")) + " = > " + StrD(ValD("0.0e0")) 
Debug "+0.0e0e0" + " = > " + Str(IsNumeric("+0.0e0e0")) + " = > " + StrD(ValD("0.0e0e0")) 
Debug "+0.0e-0e0" + " = > " + Str(IsNumeric("+0.0e-0e0")) + " = > " + StrD(ValD("0.0e-0e0")) 
Debug "+0.0e+0" + " = > " + Str(IsNumeric("+0.0e+0")) + " = > " + StrD(ValD("0.0e+0")) 
Debug "+0.0e+0e+0" + " = > " + Str(IsNumeric("+0.0e+0e+0")) + " = > " + StrD(ValD("0.0e+0e+0")) 
Debug "e" + " = > " + Str(IsNumeric("e")) + " = > " + StrD(ValD("e")) 
Debug "  99" + " = > " + Str(IsNumeric("  99"))
Debug "-99  " + " = > " + Str(IsNumeric("-99  "))
Debug "99" + " = > " + Str(IsNumeric("99"))
Debug "ABC" + " = > " + Str(IsNumeric("ABC"))
Debug "AB99CD" + " = > " + Str(IsNumeric("AB99CD"))
Debug "0000899" + " = > " + Str(IsNumeric("0000899"))
Debug "0A00899" + " = > " + Str(IsNumeric("0A00899")) 
Debug "1.64e-6" + " = > " + Str(IsNumeric("1.64e-6")) + " = > " + StrD(ValD("1.64e-6")) 
Debug "3,000" + " = > " + Str(IsNumeric("3,000")) + " = > " + StrD(ValD("3,000")) 

Debug "S => IsNumericRE(S) => ValD(S)"
Debug "  " + " = > " + Str(IsNumericRE("  ")) + " = > " + StrD(ValD("  ")) 
Debug "0" + " = > " + Str(IsNumericRE("0")) + " = > " + StrD(ValD("0")) 
Debug "0.0" + " = > " + Str(IsNumericRE("0.0")) + " = > " + StrD(ValD("0.0")) 
Debug "+0.0e0" + " = > " + Str(IsNumericRE("+0.0e0")) + " = > " + StrD(ValD("0.0e0")) 
Debug "+0.0e0e0" + " = > " + Str(IsNumericRE("+0.0e0e0")) + " = > " + StrD(ValD("0.0e0e0")) 
Debug "+0.0e-0e0" + " = > " + Str(IsNumericRE("+0.0e-0e0")) + " = > " + StrD(ValD("0.0e-0e0")) 
Debug "+0.0e+0" + " = > " + Str(IsNumericRE("+0.0e+0")) + " = > " + StrD(ValD("0.0e+0")) 
Debug "+0.0e+0e+0" + " = > " + Str(IsNumericRE("+0.0e+0e+0")) + " = > " + StrD(ValD("0.0e+0e+0")) 
Debug "e" + " = > " + Str(IsNumericRE("e")) + " = > " + StrD(ValD("e")) 
Debug "  99" + " = > " + Str(IsNumericRE("  99"))
Debug "-99  " + " = > " + Str(IsNumericRE("-99  "))
Debug "99" + " = > " + Str(IsNumericRE("99"))
Debug "ABC" + " = > " + Str(IsNumericRE("ABC"))
Debug "AB99CD" + " = > " + Str(IsNumericRE("AB99CD"))
Debug "0000899" + " = > " + Str(IsNumericRE("0000899"))
Debug "0A00899" + " = > " + Str(IsNumericRE("0A00899")) 
Debug "1.64e-6" + " = > " + Str(IsNumericRE("1.64e-6")) + " = > " + StrD(ValD("1.64e-6")) 
Debug "3,000" + " = > " + Str(IsNumericRE("3,000")) + " = > " + StrD(ValD("3,000")) 
Last edited by skywalk on Sun Nov 25, 2012 9:00 pm, edited 1 time in total.
User avatar
idle
Always Here
Always Here
Posts: 5836
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

Re: IsNumber

Post by idle »

this won't check for the format and isn't unicode

Code: Select all

Procedure IsNumeric(strexp.s)

Protected a,*valid = AllocateMemory(64)

For a = 0 To 9 
  PokeB(*valid+ct,(Asc(Str(a))))
  ct+1
Next 

PokeB(*valid+ct,Asc("."))
ct+1
PokeB(*valid+ct,Asc(","))
ct+1
PokeB(*valid+ct,Asc(":"))
ct+1
PokeB(*valid+ct,Asc("e"))
ct+1
PokeB(*valid+ct,Asc("$"))
ct+1
PokeB(*valid+ct,Asc("-"))
ct+1
PokeB(*valid+ct,Asc("+"))
ct+1 
PokeB(*valid+ct,Asc(" "))

Protected b,len, *str,*pa.byte, *pb.byte  

len = Len(strexp)

*str = @strexp
*val = *valid  
a=0
While a < len   
  *pa = *str+a 
  b=0
  While b < 18 
    *pb = *valid+b
    If *pa\b = *pb\b   
      numeric = 1
      Break  
    EndIf   
     b+1
  Wend 
  If numeric     
    cat+1
    numeric = 0
  Else 
    Break 
  EndIf 
  a+1
Wend 

FreeMemory(*valid) 

If cat = len 
  ProcedureReturn 1
Else 
  ProcedureReturn 0
EndIf     
  
EndProcedure 

Debug isNumeric("0123456789 -+e:$.,")
Debug isNumeric("0123456789 A") 
Last edited by idle on Tue Mar 23, 2010 6:57 am, edited 1 time in total.
Trond
Always Here
Always Here
Posts: 7446
Joined: Mon Sep 22, 2003 6:45 pm
Location: Norway

Re: IsNumber

Post by Trond »

Code: Select all

Structure SParseNumber
  Input.s
  Look.c
EndStructure

Procedure GetChar(*S.SParseNumber)
  *S\Look = Asc(*S\Input)
  *S\Input = Mid(*S\Input, 2)
EndProcedure

Procedure GetInteger(*S.SParseNumber)
  Protected Result = 0
  While *S\Look >= '0' And *S\Look <= '9'
    Result = 1
    GetChar(*S)
  Wend
  ProcedureReturn Result
EndProcedure

; Returns:
; 0 = syntax error
; 1 = valid integer
; 2 = valid floating point
Procedure GetFloat(*S.SParseNumber)
  IsInt = GetInteger(*S)
  If IsInt And *S\Look = '.' ; decimal point
    GetChar(*S)
    IsInt = GetInteger(*S)
    If IsInt = 0
      ProcedureReturn 0 ; syntax error
    EndIf
    ProcedureReturn 2
  EndIf
  ProcedureReturn IsInt
EndProcedure

; Returns:
; 0 = syntax error
; 1 = valid integer
; 2 = valid floating point, but not with exponent
; 3 = valid number in exponent format
Procedure GetNumberType(S.s)
  N.SParseNumber
  N\Input = S
  GetChar(@N)
  IsFloat = GetFloat(@N)
  If IsFloat And N\Look = 'e'
    GetChar(@N)
    IsInt = GetInteger(@N)
    If IsInt And N\Look = 0
      ProcedureReturn 3
    Else
      ProcedureReturn 0
    EndIf
  EndIf
  If N\Look = 0
    ProcedureReturn IsFloat
  Else
    ProcedureReturn 0
  EndIf
EndProcedure

Debug GetNumberType("0")
Debug GetNumberType("0.0")
Debug GetNumberType("0e0")
Debug GetNumberType("1234.432e89")
Debug GetNumberType("1234e89")
Debug GetNumberType("1234e89f") ; invalid
Debug GetNumberType(" 12") ; invalid
Debug GetNumberType("0 12") ; invalid
Debug GetNumberType("e12") ; invalid
Debug GetNumberType("134.23e") ; invalid
Debug GetNumberType("apple") ; invalid

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

Re: IsNumber

Post by skywalk »

Hi,
@Trond and Idle...
Not sure if your versions are complete?
They reject several examples of exponential numbers...

EDIT: By the way, Set your Debug window font to a fixed pitch font(courier new 8) for easy viewing.
I just found out this is possible! :)

Code: Select all

Structure SParseNumber
  Input.s
  Look.c
EndStructure

Procedure GetChar(*S.SParseNumber)
  *S\Look = Asc(*S\Input)
  *S\Input = Mid(*S\Input, 2)
EndProcedure

Procedure GetInteger(*S.SParseNumber)
  Protected Result = 0
  While *S\Look >= '0' And *S\Look <= '9'
    Result = 1
    GetChar(*S)
  Wend
  ProcedureReturn Result
EndProcedure

Procedure GetFloat(*S.SParseNumber)
  ; Returns:
  ; 0 = syntax error
  ; 1 = valid integer
  ; 2 = valid floating point
  IsInt = GetInteger(*S)
  If IsInt And *S\Look = '.' ; decimal point
    GetChar(*S)
    IsInt = GetInteger(*S)
    If IsInt = 0
      ProcedureReturn 0 ; syntax error
    EndIf
    ProcedureReturn 2
  EndIf
  ProcedureReturn IsInt
EndProcedure

Procedure GetNumberType(S.s)
  ; PureBasic Forum: Trond
  ; Returns:
  ; 0 = syntax error
  ; 1 = valid integer
  ; 2 = valid floating point, but not with exponent
  ; 3 = valid number in exponent format  
  N.SParseNumber
  N\Input = S
  GetChar(@N)
  IsFloat = GetFloat(@N)
  If IsFloat And N\Look = 'e'
    GetChar(@N)
    IsInt = GetInteger(@N)
    If IsInt And N\Look = 0
      ProcedureReturn 3
    Else
      ProcedureReturn 0
    EndIf
  EndIf
  If N\Look = 0
    ProcedureReturn IsFloat
  Else
    ProcedureReturn 0
  EndIf
EndProcedure

Procedure.i IsNumeric(S.s)
  ; Check only for true numeric quantities.
  ; Formatted numbers using thousands separators or currency marks are 
  ; treated as text and result in #false. 
  ; Zero is the more complicated case.
  Protected.d x
  x = ValD(Trim(S)) 
  If (x > 0 Or x < 0)
    ProcedureReturn #True
  Else  ; Check for a real "0" case.
        ; not a string converted to 0
    S = Trim(LCase(S))
    If Len(s)=0 Or (Len(S)=1 And Asc(S)<>'0')
      ProcedureReturn #False
    Else
      If FindString(S,"-",1) = 1 Or FindString(S,"+",1) = 1
        S = Mid(S,2)      
      EndIf
      S = Trim(S,"0")
      If FindString(S,".",1) = 1 
        S = Mid(S,2)  
        S = Trim(S,"0")  
        If FindString(S,".",1)
          ProcedureReturn #False
        EndIf          
      EndIf
      If FindString(S,"e",1)
        If Len(S)=1      
          ProcedureReturn #True
        EndIf
      EndIf
      If FindString(S,"e+",1) Or FindString(S,"e-",1)
        If Len(S)=2
          ProcedureReturn #True   
        EndIf
      EndIf
      If Len(S)
        ProcedureReturn #False
      Else
        ProcedureReturn #True
      EndIf
    EndIf
  EndIf
EndProcedure

Procedure.i isnumericRE(s.s)
  ; Or try using Regular Expressions...
  ; Can't get scientific notation to work :(
  Protected.s RE
  RE = "^(([0-9+-.$]{1})|([+-]?[$]?[0-9]*(([.]{1}[0-9]*)|([.]?[0-9]+))))$"
  ;RE = "[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?"
  If CreateRegularExpression(0, RE)
    If MatchRegularExpression(0, Trim(s))
      ProcedureReturn #True
    Else
      ProcedureReturn #False
    EndIf
  Else
    Debug RegularExpressionError()
  EndIf 
EndProcedure

Procedure IsNumericIdle(strexp.s)
  ; PureBasic Forum: Idle
  Protected a,*valid = AllocateMemory(16)
  For a = 0 To 9 
    PokeB(*valid+ct,(Asc(Str(a))))
    ct+1
  Next   
  PokeB(*valid+ct,Asc("."))
  ct+1
  PokeB(*valid+ct,Asc(","))
  ct+1
  PokeB(*valid+ct,Asc(":"))
  ct+1
  PokeB(*valid+ct,Asc("e"))
  ct+1
  PokeB(*valid+ct,Asc("$"))
  ct+1
  PokeB(*valid+ct,Asc("-"))
  ct+1
  PokeB(*valid+ct,Asc("+"))
  ct+1 
  PokeB(*valid+ct,Asc(" "))
  Protected b,len, *str,*pa.byte, *pb.byte  
  len = Len(strexp)
  *str = @strexp
  *val = *valid  
  a=0
  While a < len   
    *pa = *str+a 
    b=0
    While b < 18 
      *pb = *valid+b
      If *pa\b = *pb\b   
        numeric = 1
        Break  
      EndIf   
       b+1
    Wend 
    If numeric     
      cat+1
      numeric = 0
    Else 
      Break 
    EndIf 
    a+1
  Wend 
  FreeMemory(*valid) 
  If cat = len 
    ProcedureReturn 1
  Else 
    ProcedureReturn 0
  EndIf     
EndProcedure 

;-{ Load test examples
Dim T.s(50)
Define.i k,i,nsp
Define.s sp,r
Restore StringNumerics
Repeat
  Read.s T(k)
  If T(k) <> "#EOF"    
    If nsp<Len(T(k))
      nsp = Len(T(k))+1
      sp = Space(nsp)
    EndIf
    k = k + 1
  EndIf
Until T(k) = "#EOF"
ReDim T(k-1)
;-}

Debug LSet("S",nsp) + LSet("ValD(S)",nsp) + LSet("IsNumeric(S)",nsp-6) 
For i = 0 To k-1
  Debug LSet(T(i),nsp) + LSet(StrD(ValD(T(i)),6),nsp) + LSet(Str(IsNumeric(T(i))),nsp-6)  
Next i
Debug ""
Debug LSet("S",nsp) + LSet("ValD(S)",nsp) + LSet("IsNumericRE(S)",nsp-6) 
For i = 0 To k-1
  Debug LSet(T(i),nsp) + LSet(StrD(ValD(T(i)),6),nsp) + LSet(Str(IsNumericRE(T(i))),nsp-6)  
Next i
Debug ""
Debug LSet("S",nsp) + LSet("ValD(S)",nsp) + LSet("GetNumberType(S)",nsp-6) 
For i = 0 To k-1
  Debug LSet(T(i),nsp) + LSet(StrD(ValD(T(i)),6),nsp) + LSet(Str(GetNumberType(T(i))),nsp-6)  
Next i
Debug ""
Debug LSet("S",nsp) + LSet("ValD(S)",nsp) + LSet("IsNumericIdle(S)",nsp-6) 
For i = 0 To k-1
  Debug LSet(T(i),nsp) + LSet(StrD(ValD(T(i)),6),nsp) + LSet(Str(IsNumericIdle(T(i))),nsp-6)  
Next i

DataSection
StringNumerics:
  ;      S                    ValD(S)       IsNumeric(S)   
  Data.s ""                   ; 0.0         0           
  Data.s "  "                 ; 0.0         0           
  Data.s "0"                  ; 0.0         1           
  Data.s "0.0"                ; 0.0         1           
  Data.s "+0e0"               ; 0.0         1
  Data.s "+0.0e0"             ; 0.0         1           
  Data.s "+0.0e+0"            ; 0.0         1           
  Data.s "+0.0e0e0"           ; 0.0         0           
  Data.s "+0.0e-0e0"          ; 0.0         0           
  Data.s "+0.0e+0e+0"         ; 0.0         0           
  Data.s "e"                  ; 0.0         0           
  Data.s "e12"                ; 0.0         0           
  Data.s "  99"               ; 99.0        1           
  Data.s "-99  "              ; -99.0       1           
  Data.s "99"                 ; 99.0        1           
  Data.s "ABC"                ; 0.0         0           
  Data.s "AB99CD"             ; 0.0         0           
  Data.s "0000899"            ; 899.0       1           
  Data.s "0A00899"            ; 0.0         0           
  Data.s "1.64e-6"            ; 1.64e-6     1           
  Data.s "3,000"              ; 3.0         1           
  Data.s "0 12"               ; 0.0         0           
  Data.s "134.23e"            ; 134.23      1           
  Data.s "0123456789 -+e:$.," ; 123456789.0 1
  Data.s "0123456789 A"       ; 123456789.0 1
  Data.s "3,100,000.01a"      ; 3.0         1
  Data.s "3.100.000,01b"      ; 3.1         1
  Data.s "b3.000.000,01"      ; 0.0         0
  Data.s "3z,200,000.01"      ; 3.0         1 
  Data.s "3x.200.000,01"      ; 3.0         1  
  Data.s "$100.123"           ; 0.0         0 
  Data.s "#EOF"  
EndDataSection
Last edited by skywalk on Tue Jul 08, 2014 12:32 am, edited 1 time in total.
Trond
Always Here
Always Here
Posts: 7446
Joined: Mon Sep 22, 2003 6:45 pm
Location: Norway

Re: IsNumber

Post by Trond »

Hi,
@Trond and Idle...
Not sure if your versions are complete?
They reject several examples of exponential numbers...
Most of the rejected inputs I actually wanted to reject, because they're not numbers. But I did forget negative numbers.

Code: Select all

S                  ValD(S)            IsNumeric(S) GetNumberType
                   0.000000           0            0            
                   0.000000           0            0            
0                  0.000000           1            1            
0.0                0.000000           1            2            
+0e0               0.000000           1            0              ; invalid number, + is an operator, not a number
+0.0e0             0.000000           1            0            
+0.0e+0            0.000000           1            0            
+0.0e0e0           0.000000           0            0            
+0.0e-0e0          0.000000           0            0            
+0.0e+0e+0         0.000000           0            0            
e                  0.000000           0            0            
e12                0.000000           0            0            
  99               99.000000          1            0              ; invalid number, space is not a number
-99                -99.000000         1            0              ; GetNumberType wrong
99                 99.000000          1            1            
ABC                0.000000           0            0            
AB99CD             0.000000           0            0            
0000899            899.000000         1            1            
0A00899            0.000000           0            0            
1.64e-6            0.000002           1            0              ; GetNumberType wrong
3,000              3.000000           1            0              ; Invalid number, use . for decimal separator or change the code if you want to use ,
0 12               0.000000           0            0            
134.23e            134.230000         1            0              ; invalid number, exponent missing
0123456789 -+e:$., 123456789.000000   1            0              ; invalid number for many reasons
0123456789 A       123456789.000000   1            0              ; invalid number, space and A are not numbers
3,100,000.01a      3.000000           1            0              ; invalid number, a is not a number
3.100.000,01b      3.100000           1            0              ; invalid number, b is not a number
b3.000.000,01      0.000000           0            0              
3z,200,000.01      3.000000           1            0              ; invalid number, z is not a number
3x.200.000,01      3.000000           1            0              ; invalid number, x is not a number
$100.123           0.000000           0            0            
Trond
Always Here
Always Here
Posts: 7446
Joined: Mon Sep 22, 2003 6:45 pm
Location: Norway

Re: IsNumber

Post by Trond »

After adding support for negative numbers, I saw that -99 should indeed be rejected, because it contains trailing spaces, and spacei s not a number.
User avatar
skywalk
Addict
Addict
Posts: 4211
Joined: Wed Dec 23, 2009 10:14 pm
Location: Boston, MA

Re: IsNumber

Post by skywalk »

Hi Trond,
You are probably correct, but I am converting my VB6 code and I required the IsNumeric() behavior to mimic Microblob's definition. :)
VB6 => IsNumeric("+0e+0") = True
Last edited by skywalk on Tue Jul 08, 2014 12:32 am, edited 1 time in total.
Post Reply