Page 1 of 1

IsNumber() Function #5326262 :)

Posted: Wed Oct 26, 2005 8:18 pm
by Xombie
Code updated For 5.20+

My own IsNumber function to add to everyone else's. I'm using it for my xGrid project and thought it might be useful to someone. Maybe :) It has multiple returns. 1 signifies a positive integer, -1 a negative integer. 2 signifies a positive float, -2 a negative float. 0 signifies False - non-numeric. I can't remember but for some reason I thought someone else's IsNumber/IsNumeric did a similar return.

Although I have a part for hex, it's not really active. The function will test for thousands separators and decimals in strings. Optionally, you can pass the address to a string and the function will return a 'cleaned' version of the number. For example - "2,135.626" will be returned as "2135.626".

Code: Select all

Procedure IsNumber(inString.s, DecimalCharacter.b, ThousandsSeparator.b, *ReturnValue)
  ; TODO: Update to test for international decimal value and then check for IsNumeric calls to make sure they are
  ; international ready.  Maybe need a StringToSingle call first?
  ;
  ; *ReturnValue (if a LONG address is passed) will contain a 'cleaned' version of the number.  eg, no commas.
  ; IT MUST BE INITIALZED TO EMPTY OR OTHERWISE OR ELSE NOTHING WILL BE PASSED BACK.
  ;
  
  ;
  HoldCleaned.s
  ; String used to hold the 'cleaned' value.
  
  ; The count of the decimal/thousands separator.  Also the count of the numbers.
  isHex.b = #False
  ;
  
  ; The location of the decimal/thousands separator.
  IsNegative.b
  ; True if the value is negative.
  HoldChar.b
  ; This will store an individual character to test if it's numeric.
  *MemPosition = @inString
  ;
  HoldLength = Len(inString)
  ; This will store the length of our string in characters.
  Repeat
    ;
    HoldChar = PeekB(*MemPosition)
    ; Store the current character.
    If HoldChar > 47 And HoldChar < 58
      ; Numeral 0 to 9.
      If CountDecimal : CountDecimalNumerics + 1 : Else : CountNumeric + 1 : EndIf
      ;
      HoldCleaned + PeekS(*MemPosition, 1)
      ; Add the number to the 'clean' string.
    ElseIf HoldChar = 45
      ; - (negative sign)
      If iLoop > 0 : ProcedureReturn #False : EndIf
      ;
      IsNegative = #True
      ; If the minus sign is not at the front, it's not a numeral.
      HoldCleaned + PeekS(*MemPosition, 1)
      ; Add the negative sign to the 'clean' string.
    ElseIf HoldChar = 36
      ; $ (hex sign)
      If iLoop > 0 : ProcedureReturn #False : EndIf
      ; If the $ isn't at the front of the string, it's not a hex value.
      isHex = #True
      ;
    ElseIf HoldChar = ThousandsSeparator ; 44
      ; , (comma)
      If CountDecimal Or isHex : ProcedureReturn #False : EndIf
      ; Never thousands after a decimal.  Also, hex values never use a comma.
      If CountThousands And CountNumeric < 3 : ProcedureReturn #False : EndIf
      ; Thousands separator requires at least three numbers.
      CountNumeric = 0
      ; Reset the number count.
      CountThousands + 1
      ;
    ElseIf HoldChar = DecimalCharacter ; 46
      ; . (decimal sign)
      If CountDecimal > 1 : ProcedureReturn #False : EndIf
      ; If there is more than one decimal, it's not an integer.
      If isHex : ProcedureReturn #False : EndIf
      ; A hexidecimal will never have a decimal.
      If CountThousands And CountNumeric < 3 : ProcedureReturn #False : EndIf
      ; Thousands separator requires at least three numbers.
      PositionDecimal = *MemPosition - @inString
      ; Store the location of the decimal character.
      CountDecimal + 1
      ; Increment our decimal count.
      HoldCleaned + PeekS(*MemPosition, 1)
      ; Add the decimal to the 'clean' string.
    Else
      ;
      ProcedureReturn #False
      ; Unknown character, non-numeral.
    EndIf
    ;
    *MemPosition + 1
    ;
  Until *MemPosition - @inString = HoldLength
  ;
  If CountThousands And CountNumeric < 3 : ProcedureReturn #False : EndIf
  ; Thousands separator requires at least three numbers.
  If CountDecimal And CountDecimalNumerics
    ;
    If *ReturnValue : PokeS(*ReturnValue, HoldCleaned) : EndIf
    ; Return a 'cleaned' float value.
    If IsNegative : ProcedureReturn -2 : Else : ProcedureReturn 2 : EndIf
    ; Return 2 for a float or -2 for a negative float.  There must be numbers after the decimal to be considered a float.
  EndIf
  ;
  If CountNumeric
    ;
    If *ReturnValue : PokeS(*ReturnValue, HoldCleaned) : EndIf
    ; Return a 'cleaned' integer value.
    If IsNegative : ProcedureReturn -1 : Else : ProcedureReturn 1 : EndIf
    ; Return -1 for a negative integer or 1 for a positive integer.
  EndIf
  ;
  ProcedureReturn #False
  ; If we got this far, it must be a string.
EndProcedure

I tried to add useful comments for people to see what's happening. Not sure on the speed but it should be okay for a non-ASM function. Let me know if you spot bugs or just want to say hey ^_^

Posted: Thu Oct 27, 2005 3:08 am
by rsts
Many thanks for a great function.

Now I can eliminate those getnumberformat api calls that don't work with comma's anyway.

cheers

Posted: Thu Oct 27, 2005 12:42 pm
by dell_jockey
Thanks! With this one, and the expression evaluator, one is tempted to speculate about what xGrid will one day become.... No doubt very impressive!

Posted: Thu Oct 27, 2005 7:10 pm
by Straker
@Xombie - will your code be cross-platform?

Posted: Thu Oct 27, 2005 8:25 pm
by Xombie
Straker - which code? IsNumber() is shown here and doesn't use any api so it should be good to go.

If you mean xGrid, it's impossible for me, sorry :( The code is posted in my latest post on the xGrid topic (I should make a new topic for it since I'm releasing the code now) so you can see why. Callbacks and quite a few API calls, unfortunately :(

Posted: Thu Oct 27, 2005 8:40 pm
by Straker
Thanks for the reply. I guess I really meant xGrid. I just really like your code and gadgets and was hoping to use some of it on Linux.

Cheers and keep up the good work!

Posted: Fri Oct 28, 2005 5:01 pm
by dell_jockey
Xombie wrote:Straker - which code? IsNumber() is shown here and doesn't use any api so it should be good to go.

If you mean xGrid, it's impossible for me, sorry :( The code is posted in my latest post on the xGrid topic (I should make a new topic for it since I'm releasing the code now) so you can see why. Callbacks and quite a few API calls, unfortunately :(
Xombie,

you're going to release the code of xGrid?

Posted: Fri Oct 28, 2005 5:31 pm
by Xombie
dell_jockey wrote:
Xombie,

you're going to release the code of xGrid?
I already have - since this past Sunday when I got back from vacation. The older post is my last post here...

viewtopic.php?t=16852&start=30

And I just created a new topic so that it's more obvious that I've released the code. That topic is here...

viewtopic.php?p=108260

EDIT: Incidentally, I don't know if everyone knew this already but you can easily have this check for pure numbers (ie, ignore decimals and/or thousands separators) by setting DecimalCharacter and/or ThousandsSeparator to 0. If you want '2.3626' to fail as a number, pass 0 to DecimalCharacter. Or, if you want '2,323.626' to fail as a number, pass 0 to ThousandsSeparator. A simple thing and I'm sure everyone already noticed it but I thought I'd say so just in case. :oops: I wrote the thing and only just now noticed I could do that :D

IsNumber() Function #5326262 :)

Posted: Sun Oct 30, 2005 6:57 pm
by Xombie
Code updated For 5.20+

Here's an update that allows for leading and trailing spaces - basically it trims a string to check for the number. Mid-character spaces are still not allowed.

Code: Select all

Procedure IsNumber(inString.s, DecimalCharacter.b, ThousandsSeparator.b, *ReturnValue)
  ; TODO: Update to test for international decimal value and then check for IsNumeric calls to make sure they are
  ; international ready.  Maybe need a StringToSingle call first?
  ;
  ; *ReturnValue (if a LONG address is passed) will contain a 'cleaned' version of the number.  eg, no commas.
  ; IT MUST BE INITIALZED TO EMPTY OR OTHERWISE OR ELSE NOTHING WILL BE PASSED BACK.
  ;
  
  ;
  HoldCleaned.s
  ; String used to hold the 'cleaned' value.
  
  ; The count of the decimal/thousands separator.  Also the count of the numbers.
  isHex.b = #False
  ;
  
  ; The location of the decimal/thousands separator.
  CaughtSpace.b
  ; True if there is a space in the string (not including leading spaces).
  IsNegative.b
  ; True if the value is negative.
  HoldChar.b
  ; This will store an individual character to test if it's numeric.
  *MemPosition = @inString
  ;
  HoldLength = Len(inString)
  ; This will store the length of our string in characters.
  Repeat
    ;
    HoldChar = PeekB(*MemPosition)
    ; Store the current character.
    If HoldChar > 47 And HoldChar < 58
      ; Numeral 0 to 9.
      If CaughtSpace : ProcedureReturn #False : EndIf
      ; No spaces are allowed mid-string.
      If CountDecimal : CountDecimalNumerics + 1 : Else : CountNumeric + 1 : EndIf
      ;
      HoldCleaned + PeekS(*MemPosition, 1)
      ; Add the number to the 'clean' string.
    ElseIf HoldChar = 45
      ; - (negative sign)
      If CaughtSpace : ProcedureReturn #False : EndIf
      ; No spaces are allowed mid-string.
      If iLoop > 0 : ProcedureReturn #False : EndIf
      ;
      IsNegative = #True
      ; If the minus sign is not at the front, it's not a numeral.
      HoldCleaned + PeekS(*MemPosition, 1)
      ; Add the negative sign to the 'clean' string.
    ElseIf HoldChar = 36
      ; $ (hex sign)
      If CaughtSpace : ProcedureReturn #False : EndIf
      ; No spaces are allowed mid-string.
      If iLoop > 0 : ProcedureReturn #False : EndIf
      ; If the $ isn't at the front of the string, it's not a hex value.
      isHex = #True
      ;
    ElseIf HoldChar = ThousandsSeparator ; 44
      ; , (comma)
      If CaughtSpace : ProcedureReturn #False : EndIf
      ; No spaces are allowed mid-string.
      If CountDecimal Or isHex : ProcedureReturn #False : EndIf
      ; Never thousands after a decimal.  Also, hex values never use a comma.
      If CountThousands And CountNumeric < 3 : ProcedureReturn #False : EndIf
      ; Thousands separator requires at least three numbers.
      CountNumeric = 0
      ; Reset the number count.
      CountThousands + 1
      ;
    ElseIf HoldChar = DecimalCharacter ; 46
      ; . (decimal sign)
      If CaughtSpace : ProcedureReturn #False : EndIf
      ; No spaces are allowed mid-string.
      If CountDecimal > 1 : ProcedureReturn #False : EndIf
      ; If there is more than one decimal, it's not an integer.
      If isHex : ProcedureReturn #False : EndIf
      ; A hexidecimal will never have a decimal.
      If CountThousands And CountNumeric < 3 : ProcedureReturn #False : EndIf
      ; Thousands separator requires at least three numbers.
      PositionDecimal = *MemPosition - @inString
      ; Store the location of the decimal character.
      CountDecimal + 1
      ; Increment our decimal count.
      HoldCleaned + PeekS(*MemPosition, 1)
      ; Add the decimal to the 'clean' string.
    Else
      ;
      If HoldChar = 32
        ; Character is an empty character value.
        If CountDecimal = 0 And CountThousands = 0 And CountNumeric = 0
          ; Ignore leading empty spaces.
        Else
          ;
          CaughtSpace = #True
          ; We'll allow spaces at the end - not spaces between characters.
        EndIf
        ;
      Else
        ;
        ProcedureReturn #False
        ; Unknown character, non-numeral.
      EndIf
      ;
    EndIf
    ;
    *MemPosition + 1
    ;
  Until *MemPosition - @inString = HoldLength
  ;
  If CountThousands And CountNumeric < 3 : ProcedureReturn #False : EndIf
  ; Thousands separator requires at least three numbers.
  If CountDecimal And CountDecimalNumerics
    ;
    If *ReturnValue : PokeS(*ReturnValue, HoldCleaned) : EndIf
    ; Return a 'cleaned' float value.
    If IsNegative : ProcedureReturn -2 : Else : ProcedureReturn 2 : EndIf
    ; Return 2 for a float or -2 for a negative float.  There must be numbers after the decimal to be considered a float.
  EndIf
  ;
  If CountNumeric
    ;
    If *ReturnValue : PokeS(*ReturnValue, HoldCleaned) : EndIf
    ; Return a 'cleaned' integer value.
    If IsNegative : ProcedureReturn -1 : Else : ProcedureReturn 1 : EndIf
    ; Return -1 for a negative integer or 1 for a positive integer.
  EndIf
  ;
  ProcedureReturn #False
  ; If we got this far, it must be a string.
EndProcedure