Overflow Error evaluating arithmetic expression with integers in VBA - vba

The following evaluates to 32760, an integer:
Debug.Print (20 * 1638 - 1)
But this raises an overflow error:
Dim t as Integer
t = 1639
Debug.Print (20 * t - 1)
It seems like this is implicitly expecting an Integer return value, because if I do either of the following, the error is avoided.
Dim t as Long OR Debug.print (20 * CLng(t) - 1) OR Debug.Print (20# * t - 1)
Is this behavior documented?
Is my assumption accurate? Namely, that arithmetic of integers implies an integer return value, and that simply introducing one Long or Double value in to the equation will avoid the error?

If my logic is correct, Dim or 'Dimension' is a way of telling the application that you expect use a variable of a certain type, and that type pertains to a certain amount of 'bits' (of memory).
This reserves a section of the system's memory, which has been allocated a certain amount of bits dependant on the variable type that you have instructed in your code. These bits then define how many (If you're familiar with C++ or similar then you will probably already know all this...)
An Integer is 16 bits in VBA and is a signed integer which means we can store negative values too, so the limit is 32,767 because this is the biggest number we can achieve with 16 bits:
(generally a variable can hold 2^n where n = number of bits)
unsigned 16 bits = 0 - 65,536 (2^16)
signed 16 bits = -32,768 - 32,767
32,767 = 111111111111111 (Binary)
32,768 = 1000000000000000 <--- note the extra bit
This extra bit is what causes the "overflow" error - because the amount of bits required to produce the number overflows the amount of bits that the memory has to store the number safely.
I don't think the method of the calculation is documented to this extent, however your code snippet:
Dim t as Integer
t = 1639
Debug.Print (20 * t - 1)
would require t to be first be multiplied by 20, resulting in a figure of 32,780:
20 * t = 20 * 1639 = 32,780
32,780 = 1000000000001100 (Binary)
which overflows the bit limit for the Integer data type. At this point the system throws an error before it has the chance to proceed with the rest of the calculation because it tries to multiply t whilst still in it's allocated memory address, for which only 16 bits of memory have been reserved.
Also, not declaring t as a type will force VBA to default to type Variant which will assess that t needs to have more memory allocated when the calculation runs and push it into the Long boundary automatically.
Update: It would appear that VBA will only permit the highest amount of bits held by a variable within the equation for the return value, as can be seen in this example:
Sub SO()
Dim t As Integer, c As Long
t = 1639
c = 20
Debug.Print (20 * (t - 1)) '// No Error
Debug.Print (c * (t - 1)) '// No Error
Debug.Print ((c * t) - 1) '// No Error
c = (20 * t - 1) '// Error
Debug.Print (20 * t - 1) '// Error
End Sub
Although I don't believe this is documented anywhere, it would lead one to believe that VBA limits memory usage to the highest amount of bits being used by a variable at any one time.

Related

Why does re-ordering the operands in VBA avoid "Run-time Error 6: Overflow"?

I just want to know the reason for the run-time error: Overflow when running these VBA codes
Function target_time(Time) As Double
target_time = 1200 * 60 * Time
End Function
Sub Test()
x = target_time(24)
MsgBox x
End Sub
I already addressed this issue by rearranging the function formula into:
target_time = Time*1200 * 60
I just want to know why is repositioning the variable causes the runtime error overflow
Here's what VBA does:
1200 and 60 <-- Those are small enough to be a (16-bit) Integer, so I'll assume they are 16-bit Integer constants.
1200 * 60 <-- A multiplication of two 16-bit Integers yields an Integer - there is no automatic promotion to a 32-bit Long. Since 1200 * 60 = 72000 exceeds the maximum value of a 16-bit integer, the expression overflows.
On the other hand, Time * 1200 is not a multiplication of two 16-bit Integers, it's a multiplication of a "Variant containing an Integer" with an Integer. In this case, the result is automatically promoted to a "Variant containing a Long":
Dim v As Variant
v = 1200
Debug.Print TypeName(v) ' prints Integer
Debug.Print TypeName(v * 60) ' prints Long
Debug.Print TypeName(v * 6000000) ' prints Double(!)
The reason for this can be found in section 5.6.9.3 of the VBA spec:
If one or both operands have a declared type of Variant:
If the operator’s effective value type is Integer, Long, Single or Double, the operator’s
value type is the narrowest type of either Integer, Long or Double such that the
operator value is within the valid range of the type. If the result does not fit within
Double, runtime error 6 (Overflow) is raised.
As an additon to Heinzi's answer which addresses the OP's question I would like to add that I would add some of the following operators: CLng, CDbl to make sure that the result will be correct (or better closer to correct :-) )
Instead of target_time = 1200 * 60 * Time use target_time = CDbl(1200) * CDbl(60) * CDbl(Time).

I am getting the error "Overflow." in Visual Basic 2010

Dim clicks As Integer
clicks = 0
If clicks >= 10000000000000000000 Then
a19.ForeColor = Color.FromArgb(0, 153, 0)
End If
10000000000000000000 and above gives the error.
Is there any way to have unlimited large length numbers?
You can use Long instead of Integer to allow you to go up to about 9 * 10E18 (9 followed by 18 zeros). Use the L suffix on literals if they are Long
Dim bigNumber as Long = 5000000000000000000L
If you want integers that can be any size at all, look at the BigInteger Structure. For example (the following requires a reference to the System.Numerics DLL as well as the statement Imports System.Numerics
Dim clicks As BigInteger = 0
'Code that updates click goes here
Dim limit As BigInteger = BigInteger.Parse("10000000000000000000")
If clicks >= limit Then
a19.ForeColor = Color.FromArgb(0, 153, 0)
End If
You can also use Double to hold number as large as 1E308, but it can't hold 308 decimal digits, so the numbers will only be approximations.
In Computer Science a number is most often represented as an integer. An integer is most often defined as a 32-bit signed integer, which means that the value can only hold up to a value from -(2^31) to (2^31)-1. To use bigger numbers, you might want to check other integer types. The most commonly used bigger integer, is the 64-bit integer, which can hold a value from -(2^63) to (2^63)-1.

VBA overflow error with byte variables

Can someone please explain why the following code would generate an overflow error in VBA when the recipient of the operation c is an Integer?
Dim a As byte, b As Byte
Dim c As Integer
a = 3: b = 100
c = a * b
or does it mean that every operation involving 'Byte` variables would have to yield only a result be between 0 and 255 regardless of the recipient variable type?
or does it mean that every operation involving byte variables would have to yield only a result be between 0 and 255 regardless of the recipient variable type
Yes, because Bytes only hold values from 0 to 255, multiplying 3 x 100, you are passing (overflowing) its capacity, even though afterwards you are passing the result into an integer.
Because you are multiplying two Bytes together, VBA assumes the result to be a Byte too. It is only after the calculation that the result is then cast to an integer.
To get around this, you must cast at least one of the variables. This lets VBA know that it must make room for a larger result:
Dim a As Byte, b As Byte
Dim c As Integer
a = 3
b = 100
c = a * CInt(b) ' <-- Cast b as Integer to prevent Overflow error

How to fine the range of a number?

The valid range of a Byte variable is 0 and 255.
Dim b As Byte
b = 30
' The following statement causes an error because the value is too large.
'b = 256
how to find the range of a number like 30 = 256
I may be misunderstanding your question, but have you looked at the documentation for data types? http://msdn.microsoft.com/en-us/library/vstudio/47zceaw7%28v=vs.120%29.aspx
The documentation explicitly states the valid ranges for all data types.

Overflow when multiplying Integers and assigning to Long

If I type the following into the immediate window I get Runtime error '6': Overflow.
MsgBox 24 * 60 * 60
Why is this?
This also fails:
Dim giveTime As Long
giveTime = 24 * 60 * 60
Why is this? giveTime is declared as a Long type, so 24 × 60 × 60 = 86400 should comfortably fit.
This is a really odd VBA quirk. I'm amazed I've never bumped into this.
Dim x As Long
x = 24 * 60 * 60 ' Overflow
x = 32767 + 1 ' Overflow.
x = 32768 + 1 ' Works fine!
So it looks like the * and + operators return an Integer in the first two examples. Sure enough, in the help file for the *operator (similar for the + operator):
result = number1 * number2
[...]
The data type of result is usually the same as that of the most precise expression.
Your literals 24, 60, and 60 are all of type Integer by default, so your * (or +) operator returns an Integer, which overflows because the result is greater than 32,767.
However, the literal 32,768 in the third example above defaults to Long type (since it is too large to be an Integer) and so the + returns a Long; no overflow.
The help file also says this:
If [...] the data type of result is an Integer variant that overflows its legal range [...] Then result is [...] converted to a Long variant.
Emphasis mine. Now this little rule sounds like common sense, and anyone would reasonably assume that it applies in your case. But your numbers are of type Integer, not Variant/Integer, so VBA doesn't apply this rule! Makes absolutely no sense to me, but that's how it is, and that's what the documentation says.
Solution: make one of the arguments of your * operator be of a type more precise than Integer (e.g. Long) and the problem will go away.
x = CLng(24) * 60 * 60 ' Result is Long, works fine.
In fact, and this is probably why I've never bumped into this quirk, I make a habit of declaring all of my Integer variables as Long instead, unless there is a specific concern that having Longs instead of Integers will cause problems with memory use or execution time (which is almost never the case). Granted, this won't help in cases when you operate on literals smaller than 32,768, because they default to Integer type.
You ask in a comment what a Variant/Integer is. Variant is basically a container type for any other data type. In the particular case where you make it contain an Integer:
Dim a As Variant ' a is now Empty
a = CInt(32767) ' a is now Variant/Integer
x = a + 1 ' works fine
But as noted above, a plain old Integer triggers the overflow error:
Dim b As Integer
b = 32767
x = b + 1 ' overflow
After every number, place #. It defines each number as a double. Think of it as, each number is placed in to memory for the calculation as sort of a temp variable.
If you define each number, it will allow enough space for the calculations.
eg:
Dim x As Long
x = 24# * 60# * 60#
or 24& 'indicates long