Request for critique of my IsInt function

mdlueck

Sr. Application Developer
Local time
Today, 18:09
Joined
Jun 23, 2011
Messages
2,650
Please advise if you know of a better way to optimize this "Is Integer" checking function:

Code:
Public Function datatypevalidation_IsInt(ByVal varNumber As Variant) As Boolean
  On Error GoTo Err_datatypevalidation_IsInt

  'Assume invalid data
  datatypevalidation_IsInt = False

  'General Numeric test
  If Not IsNumeric(varNumber) Then
    GoTo Exit_datatypevalidation_IsInt
  End If

  'Specific Interger datatype test
  If CStr(varNumber) <> CStr(CInt(varNumber)) Then
    GoTo Exit_datatypevalidation_IsInt
  End If

  'Checks out valid
  datatypevalidation_IsInt = True

Exit_datatypevalidation_IsInt:
  Exit Function

Err_datatypevalidation_IsInt:
  Call errorhandler_MsgBox("Module: modshared_datatypevalidation, Function: datatypevalidation_IsInt()")
  Resume Exit_datatypevalidation_IsInt

End Function
Within it, the first check weeds out non-numeric values.

The second leverages CInt() which is not safe to pass non-numeric values to. So I am thinking I am forced into performing two checks.
 
So what is the purpose of this, are you only wanting to see if the variable passed in was an integer type, of if any number passed in could qualify to be an integer?

I.E.

If I pass a long variable with a value of 1500 your function would return true, even though it is not really an "integer" but a "long", however if I pass a long with a value of 65000, it fails and returns false because 65000 exceeds the value of the Interger datatype. Is this the intent?
 
If I pass a long variable with a value of 1500 your function would return true, even though it is not really an "integer" but a "long", however if I pass a long with a value of 65000, it fails and returns false because 65000 exceeds the value of the Interger datatype. Is this the intent?

Correct.

Say I am reading a user created import file. (read "text file" here...) This test should be able to verify that the data coming into the system is valid to put into an Integer type variable or database column.
 
Cool, I would go with something a little simpler, but yours works just fine.

Code:
Public Function datatypevalidation_IsInt(ByVal varNumber As Variant) As Boolean
  On Error GoTo Err_datatypevalidation_IsInt

  Debug.Assert CInt(varNumber)
  
  datatypevalidation_IsInt = True

Exit_datatypevalidation_IsInt:
  Exit Function

Err_datatypevalidation_IsInt:
  Call errorhandler_MsgBox("Module: modshared_datatypevalidation, Function: datatypevalidation_IsInt()")
  Resume Exit_datatypevalidation_IsInt

End Function
 
Doing a bit of my own torture testing, I managed to crash the code giving it a number larger than Integers can store. I added special handling for that situation.

Code:
Public Function datatypevalidation_IsInt(ByVal varNumber As Variant) As Boolean
  On Error GoTo Err_datatypevalidation_IsInt

  'Assume invalid data
  datatypevalidation_IsInt = False

  'General Numeric test
  If Not IsNumeric(varNumber) Then
    GoTo Exit_datatypevalidation_IsInt
  End If

  'Specific Interger datatype test
  If CStr(varNumber) <> CStr(CInt(varNumber)) Then
    GoTo Exit_datatypevalidation_IsInt
  End If

  'Checks out valid
  datatypevalidation_IsInt = True

Exit_datatypevalidation_IsInt:
  Exit Function

Err_datatypevalidation_IsInt:
[B]  If Err.Number <> 6 Then '6 = Overflow which is anticipated
[/B]    'Further error handling here for other error types
    Call errorhandler_MsgBox("Module: modshared_datatypevalidation, Function: datatypevalidation_IsInt()")
  End If

  Resume Exit_datatypevalidation_IsInt

End Function
What does your suggested Debug.Assert do?
 
What does your suggested Debug.Assert do?

rrr... Does not look like something I should use in production code... ???

Assert invocations work only within the development environment. When the module is compiled into an executable, the method calls on the Debug object are omitted.
 
Indeed, if you are compiling into an MDE I would not use the debug.assert. The point of my code was to cause an error if the Cint failed, then exit if it does. It is faster to just check if it converts to an integer and handle the error than to check all possible reasons it may or may not be an integer.
 
You can very reliably use the Typename() function ...
Code:
Function IsInteger(Expression) As Boolean
    IsInteger = TypeName(Expression) = "Integer"
End Function
 
Thank you, lagbolt! My implementation:

Code:
Public Function datatypevalidation_IsInt(ByVal varExpression As Variant) As Boolean

  datatypevalidation_IsInt = TypeName(varExpression) = "Integer"

End Function
And judging from my quick glance at the TypeName documentation, I dare say I can easily craft a bunch of other data type validation functions.
 
One possible issue with this implementation is if you pass in a predefined data type.
Code:
sub Test
dim x as long
x = 100
debug.print datatypevalidation_IsInt(x)
end sub
This would return false even though x qualifies as an integer, because the data type is explicitly a long.
 
Thank you DJkarl. Tested / confirmed a problem.

I shall revert to my more verbose implementation (#5) for now.
 
Yeah, you bet. And maybe you want to return True for Long datatypes that are within the Min and Max integer values...
Code:
Function IsInteger(Expression) As Boolean
    If TypeName(Expression) = "Integer" Then
[COLOR="Green"]        'returns True for integers[/COLOR]
        IsInteger = True
    ElseIf TypeName(Expression) = "Long" Then
[COLOR="Green"]        'returns True for a Long within Min and Max integer[/COLOR]
        IsInteger = Expression >= -32768 And Expression <= 32767
    End If
End Function
 
Oh, looks like you've been thinking along the same lines... :)
 
OK, based on #12 I have modified my solution to the following:

Code:
Public Function datatypevalidation_IsInt(ByVal varExpression As Variant) As Boolean
  On Error GoTo Err_datatypevalidation_IsInt

  Dim strTypeName As String

  'Assume invalid data
  datatypevalidation_IsInt = False

  'Call TypeName to determine the type of object passed in
  strTypeName = TypeName(varExpression)

  Select Case strTypeName
    Case "Integer"
      'Return True for Integers
      datatypevalidation_IsInt = True
    Case "Long"
      'Return True for a Long within Min/Max of Integer
      datatypevalidation_IsInt = (varExpression >= -32768) _
                                 And (varExpression <= 32767)
  End Select

Exit_datatypevalidation_IsInt:
  Exit Function

Err_datatypevalidation_IsInt:
  Call errorhandler_MsgBox("Module: modshared_datatypevalidation, Function: datatypevalidation_IsInt()")
  Resume Exit_datatypevalidation_IsInt

End Function
Using a Case statement I only make one call to the TypeName function.
 
oops! I forgot the Byte datatype. That can also fit within an Integer datatype...

Code:
Public Function datatypevalidation_IsInt(ByVal varExpression As Variant) As Boolean
  On Error GoTo Err_datatypevalidation_IsInt

  Dim strTypeName As String

  'Assume invalid data
  datatypevalidation_IsInt = False

  'Call TypeName to determine the type of object passed in
  strTypeName = TypeName(varExpression)

  Select Case strTypeName
    Case "Integer"
      'Return True for Integers
      datatypevalidation_IsInt = True
    Case "Byte"
      'Return True for Bytes as they are smaller than Integers
      datatypevalidation_IsInt = True
    Case "Long"
      'Return True for a Long within Min/Max of Integer
      datatypevalidation_IsInt = (varExpression >= -32768) _
                                 And (varExpression <= 32767)
  End Select

Exit_datatypevalidation_IsInt:
  Exit Function

Err_datatypevalidation_IsInt:
  Call errorhandler_MsgBox("Module: modshared_datatypevalidation, Function: datatypevalidation_IsInt()")
  Resume Exit_datatypevalidation_IsInt

End Function

Thus the reason I decided to use Case and a single call to TypeName.
 
Well I kept finding things that potenially could be Integers... ;)

Code:
Public Function datatypevalidation_IsInt(ByVal varExpression As Variant) As Boolean
  On Error GoTo Err_datatypevalidation_IsInt

  Dim strTypeName As String

  'Assume invalid data
  datatypevalidation_IsInt = False

  'Call TypeName to determine the type of object passed in
  strTypeName = TypeName(varExpression)

  Select Case strTypeName
    Case "Integer"
      'Return True for Integers
      datatypevalidation_IsInt = True
    Case "Byte"
      'Return True for Bytes as they are smaller than Integers
      datatypevalidation_IsInt = True
    Case "Boolean"
      'Return True for Boolean as they are smaller than Integers
      datatypevalidation_IsInt = True
    Case "Long"
      'Return True for a Long within Min/Max of Integer
      datatypevalidation_IsInt = (varExpression >= -32768) _
                                 And (varExpression <= 32767)
    Case "Single", "Double", "Currency", "Decimal", "String"
      'Return True for a successful intensive evaluation...
      datatypevalidation_IsInt = (CStr(varExpression) = CStr(CInt(varExpression))) _
                                 And (varExpression >= -32768) _
                                 And (varExpression <= 32767)

  End Select

Exit_datatypevalidation_IsInt:
  Exit Function

Err_datatypevalidation_IsInt:
  If Err.Number <> 6 Then '6 = Overflow which is anticipated
    'Further error handling here for other error types
    Call errorhandler_MsgBox("Module: modshared_datatypevalidation, Function: datatypevalidation_IsInt()")
  End If

  Resume Exit_datatypevalidation_IsInt

End Function
I am having difficulty obtaining a sample of a Decimal datatype as there is no VBA varibal type specifically for Decimal. MS maps it to the Variant datatype. Any guesses how to prepare a variable such that TypeName would actually return "Decimal"? Is it merely based on a number being large enough to qualify as nothing smaller than a Decimal?

I believe the rest of the conditions I have been able to successfully test.
 
Last edited:
I found a technique to produce a real Decimal...

Code:
  Dim varX As Variant

  varX = CDec("79,228,162,514,264,337,593,543,950,335")
  Debug.Print datatypevalidation_IsInt(varX)
Placing a watch on varExpression within the function identifies the Variant as Decimal type.

And the current code is as follows:

Code:
Public Function datatypevalidation_IsInt(ByVal varExpression As Variant) As Boolean
  On Error GoTo Err_datatypevalidation_IsInt

  Dim strTypeName As String

  'Assume invalid data
  datatypevalidation_IsInt = False

  'Call TypeName to determine the type of object passed in
  strTypeName = TypeName(varExpression)

  Select Case strTypeName
    Case "Integer"
      'Return True for Integers
      datatypevalidation_IsInt = True
    Case "Byte"
      'Return True for Bytes as they are smaller than Integers
      datatypevalidation_IsInt = True
    Case "Boolean"
      'Return True for Boolean as they are smaller than Integers
      datatypevalidation_IsInt = True
    Case "Long"
      'Return True for a Long within Min/Max of Integer
      datatypevalidation_IsInt = (varExpression >= -32768) _
                                 And (varExpression <= 32767)
    Case "Single", "Double", "Currency", "Decimal", "String"
      'Return True for a successful intensive evaluation...
      datatypevalidation_IsInt = (CStr(varExpression) = CStr(CInt(varExpression)))

  End Select

Exit_datatypevalidation_IsInt:
  Exit Function

Err_datatypevalidation_IsInt:
  If Err.Number <> 6 Then '6 = Overflow which is anticipated
    'Further error handling here for other error types
    Call errorhandler_MsgBox("Module: modshared_datatypevalidation, Function: datatypevalidation_IsInt()")
  End If

  Resume Exit_datatypevalidation_IsInt

End Function
 
You can combine your Case for Integer, Byte and Boolean ...
Code:
   Select Case Typename(Expression)
      Case "Integer", "Byte", "Boolean"
         IsInteger = True
 
Thank you lagbolt. Excellent optimization find. I actually spotted two optimizations in your suggestion:

1) Combine "Integer", "Byte", "Boolean"
2) Have TypeName(varExpression) feed directly into the Select Case statement

As I now have it...

Code:
Public Function datatypevalidation_IsInt(ByVal varExpression As Variant) As Boolean
  On Error GoTo Err_datatypevalidation_IsInt

  Select Case TypeName(varExpression)
    Case "Integer", "Byte", "Boolean"
      'Return True for Integers / Bytes and Boolean as they are smaller than Integers
      datatypevalidation_IsInt = True
    Case "Long"
      'Return True for a Long within Min/Max of Integer
      datatypevalidation_IsInt = (varExpression >= -32768) _
                                 And (varExpression <= 32767)
    Case "Single", "Double", "Currency", "Decimal", "String"
      'Return True for a successful intensive evaluation...
      datatypevalidation_IsInt = (CStr(varExpression) = CStr(CInt(varExpression)))
  End Select

Exit_datatypevalidation_IsInt:
  Exit Function

Err_datatypevalidation_IsInt:
  If Err.Number <> 6 Then '6 = Overflow which is anticipated
    'Further error handling here for other error types
    Call errorhandler_MsgBox("Module: modshared_datatypevalidation, Function: datatypevalidation_IsInt()")
  End If

  Resume Exit_datatypevalidation_IsInt

End Function
 
michael - what exactly are you trying to do with this function - just confirm you have a integer?

in which case

function isint(v as variant) as boolean
dim i as integer

on error goto fail
 

Users who are viewing this thread

Back
Top Bottom