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
Related
I spent a couple of days to communicate with the PLC, now, I would like to call the read method, strangely, I get an incoherent value returned.
I would like to get the DWord DB172.DW1 value, the first value represents a quantity, the second one is either 0 or 1, my syntax is like this :
Dim result as Object = MyPLC.read(“DB172.DBW1″)
MsgBox(result.ToString & ” ” & result.GetType.ToString)
I obtain as displayed message :
12 System.UInt16
On DB172.DBW2, I obtain this one :
3073 System.UInt16
What am I doing wrong? Thanks
It looks like MyPLC returns System.UInt16, which is two bytes. The hex value of 12 is C and of 3073 is C01. It appears that MyPLC(“DB172.DBW1″) just returns the high order byte into a System.UInt16 value and MyPLC(“DB172.DBW2″) returns both values. You can try:
Dim u1 As System.UInt16 = MyPLC("DB172.DBW1")
Dim b1() As Byte = BitConverter.GetBytes(u1)
Dim u2 As System.UInt16 = MyPLC("DB172.DBW2")
Dim b2() As Byte = BitConverter.GetBytes(u2)
and examine the values in the arrays
Many thanks Jim for your reply, actually, it's a little more complex than I thought.
Actually, to read the DWord number x, the first value is obtained by querying DB172.DBW(2x), the second value is obtained by querying DB172.DBW(2x+1)
So, for example, to read the DWord number 10, the 2 values are obtained like this :
Dim Value1 as byte = MyPLC.read(DB172.DBDW20")
Dim Value2 as byte = MyPLC.read(DB172.DBDW21")
I have written a short function to calculate a power of a number modulo N. I can get out of the range of any integer like type, so I've written a loop, that calculates only modulus values:
Function ModPwr(b As Integer, e As Integer, m As Integer) As Integer
Dim r As Integer
r = 1
Do While e > 0
r = r * b Mod m
e = e - 1
Loop
ModPwr = r
End Function
The function collapses or return zero for some input (eg: 217, 143, 221). If I completly remove all "As Integer" and the Dim declaration, everything works fine:
Function ModPwr(b, e, m)
r = 1
Do While e > 0
r = r * b Mod m
e = e - 1
Loop
ModPwr = r
End Function
I have to implement an RSA demo in Excel to demonstrate the encryption/decryption method (using small primes!). I am a newby in VBA.
In other (more complicated) functions I need the type declaration, so I have to find the problem.
In VBA, an integer can have a maximum value of 32,767. I'm guessing you're exceeding that and getting an error.
When you don't declare your variable As Integer, it is automatically created as type Variant which has a much larger maximum value.
Your data type needs to be able to handle up to m * m. The Integer datatype has a maximum value of 2^15-1 (or 32,767), so can handle m up to 181.
The default data type works because it's double which has a large range, but is also not an exact numeric. Whet you probably want is to declare everything as Decimal which has a range up to 10^28, so can handle m up to 100,000,000,000,000 and is an exact numeric.
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.
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.
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