I often see questions relating to Overflow errors with vba.
My question is why use the integer variable declaration instead of just defining all numerical variables (excluding double etc.) as long?
Unless you're performing an operation like in a for loop where you can guarantee that the value won't exceed the 32,767 limit, is there an impact on performance or something else that would dictate not using long?
Integer variables are stored as 16-bit (2-byte) numbers
Office VBA Reference
Long (long integer) variables are stored as signed 32-bit (4-byte) numbers
Office VBA Reference
So, the benefit is in reduced memory space. An Integer takes up half the memory that a Long does. Now, we are talking about 2 bytes, so it's not going to make a real difference for individual integers, it's only a concern when you are dealing with TONS of integers (e.g large arrays) and memory usage is critical.
BUT on a 32 bit system, the halved memory usage comes at a performance cost. When the processor actually performs some computation with a 16 bit integer (e.g. incrementing a loop counter), the value silently gets converted to a temporary Long without the benefit of the larger range of numbers to work with. Overflows still happen, and the register that the processor uses to store the values for the calculation will take the same amount of memory (32 bits) either way. Performance may even be hurt because the datatype has to be converted (at a very low level).
Not the reference I was looking for but....
My understanding is that the underlying VB engine converts integers to long even if its declared as an integer. Therefore a slight speed decrease can be noted. I have believed this for some time and perhaps thats also why the above statement was made, I didnt ask for reasoning.
ozgrid forums
This is the reference I was looking for.
Short answer, in 32-bit systems 2 byte integers are converted to 4 byte
Longs. There really is no other way so that respective bits correctly line
up for any form of processing. Consider the following
MsgBox Hex(-1) = Hex(65535) ' = True
Obviously -1 does not equal 65535 yet the computer is returning the correct
answer, namely
"FFFF" = "FFFF"
However had we coerced the -1 to a long first we would have got the right
answer (the 65535 being greater than 32k is automatically a long)
MsgBox Hex(-1&) = Hex(65535) ' = False
"FFFFFFFF" = "FFFF"
Generally there is no point in VBA to declare "As Integer" in modern
systems, except perhaps for some legacy API's that expect to receive an
Integer.
pcreview forum
And at long last I found the msdn documentation I was really truly looking for.
Traditionally, VBA programmers have used integers to hold small
numbers, because they required less memory. In recent versions,
however, VBA converts all integer values to type Long, even if they're
declared as type Integer. So there's no longer a performance advantage
to using Integer variables; in fact, Long variables may be slightly
faster because VBA does not have to convert them.
To clarify based on the comments: Integers still require less memory to store - a large array of integers will need significantly less RAM than an Long array with the same dimensions. But because the processor needs to work with 32 bit chunks of memory, VBA converts Integers to Longs temporarily when it performs calculations
So, in summary, there's almost no good reason to use an Integer type these days. Unless you need to Interop with an old API call that expects a 16 bit int, or you are working with large arrays of small integers and memory is at a premium.
One thing worth pointing out is that some old API functions may be expecting parameters that are 16-bit (2-byte) Integers and if you are on a 32 bit and trying to pass an Integer (that is already a 4-byte long) by reference it will not work due to difference in length of bytes.
Thanks to Vba4All for pointing that out.
Even though this post is four years old, I was curious about this and ran some tests. The most important thing to note is that a coder should ALWAYS declare a variable as SOMETHING. Undeclared variables clearly performed the worst (undeclared are technically Variant)
Long did perform the fastest, so I have to think that Microsoft's recommendation to always use Long instead of Integer makes sense. I'm guessing the same as true with Byte, but most coders don't use this.
RESULTS ON 64 BIT WINDOWS 10 LAPTOP
Code Used:
Sub VariableOlymics()
'Run this macro as many times as you'd like, with an activesheet ready for data
'in cells B2 to D6
Dim beginTIME As Double, trials As Long, i As Long, p As Long
trials = 1000000000
p = 0
beginTIME = Now
For i = 1 To trials
Call boomBYTE
Next i
Call Finished(p, Now - beginTIME, CDbl(trials))
p = p + 1
beginTIME = Now
For i = 1 To trials
Call boomINTEGER
Next i
Call Finished(p, Now - beginTIME, CDbl(trials))
p = p + 1
beginTIME = Now
For i = 1 To trials
Call boomLONG
Next i
Call Finished(p, Now - beginTIME, CDbl(trials))
p = p + 1
beginTIME = Now
For i = 1 To trials
Call boomDOUBLE
Next i
Call Finished(p, Now - beginTIME, CDbl(trials))
p = p + 1
beginTIME = Now
For i = 1 To trials
Call boomUNDECLARED
Next i
Call Finished(p, Now - beginTIME, CDbl(trials))
p = p + 1
End Sub
Private Sub boomBYTE()
Dim a As Byte, b As Byte, c As Byte
a = 1
b = 1 + a
c = 1 + b
c = c + 1
End Sub
Private Sub boomINTEGER()
Dim a As Integer, b As Integer, c As Integer
a = 1
b = 1 + a
c = 1 + b
c = c + 1
End Sub
Private Sub boomLONG()
Dim a As Long, b As Long, c As Long
a = 1
b = 1 + a
c = 1 + b
c = c + 1
End Sub
Private Sub boomDOUBLE()
Dim a As Double, b As Double, c As Double
a = 1
b = 1 + a
c = 1 + b
c = c + 1
End Sub
Private Sub boomUNDECLARED()
a = 1
b = 1 + a
c = 1 + b
c = c + 1
End Sub
Private Sub Finished(i As Long, timeUSED As Double, trials As Double)
With Range("B2").Offset(i, 0)
.Value = .Value + trials
.Offset(0, 1).Value = .Offset(0, 1).Value + timeUSED
.Offset(0, 2).FormulaR1C1 = "=ROUND(RC[-1]*3600*24,0)"
End With
End Sub
As noted in other answers, the real difference between int and long is the size of its memory space and therefore the size of the number it can hold.
here is the full documentation on these datatypes
http://msdn.microsoft.com/en-us/library/office/ms474284(v=office.14).aspx
an Integer is 16 bits and can represent a value between -32,768 and 32,767
a Long is 32 bits and can represent -2,147,483,648 to 2,147,483,647
and there is a LongLong which is 64 bits and can handle like 9 pentilion
One of the most important things to remember on this is that datatypes differ by both language and operating system / platform. In your world of VBA a long is 32 bits, but in c# on a 64 bit processor a long is 64 bits. This can introduce significant confusion.
Although VBA does not have support for it, when you move to any other language in .net or java or other, I much prefer to use the system datatypes of int16, int32 and int64 which allows me to b much more transparent about the values that can be held in these datatypes.
VBA has a lot of historical baggage.
An Integer is 16 bits wide and was a good default numeric type back when 16 bit architecture/word sizes were prevalent.
A Long is 32 bits wide and (IMO) should be used wherever possible.
I have taken #PGSystemTester's method and updated it to remove some potential variability. By placing the loop in the routines, this removes the time taken to call the routine (which is a lot of time). I have also turned off screen updating to remove any delays this may cause.
Long still performed the best, and as these results are more closely limited to the impacts of the variable types alone, the magnitude of variation is worth noting.
My results (desktop, Windows 7, Excel 2010):
Code used:
Option Explicit
Sub VariableOlympics()
'Run this macro as many times as you'd like, with an activesheet ready for data
'in cells B2 to D6
Dim beginTIME As Double, trials As Long, i As Long, p As Long
Dim chosenWorksheet As Worksheet
Set chosenWorksheet = ThisWorkbook.Sheets("TimeTrialInfo")
Application.EnableEvents = False
Application.Calculation = xlCalculationManual
Application.ScreenUpdating = False
trials = 1000000000 ' 1,000,000,000 - not 10,000,000,000 as used by #PGSystemTester
p = 0
beginTIME = Now
boomBYTE trials
Finished p, Now - beginTIME, CDbl(trials), chosenWorksheet.Range("B2")
p = p + 1
beginTIME = Now
boomINTEGER trials
Finished p, Now - beginTIME, CDbl(trials), chosenWorksheet.Range("B2")
p = p + 1
beginTIME = Now
boomLONG trials
Finished p, Now - beginTIME, CDbl(trials), chosenWorksheet.Range("B2")
p = p + 1
beginTIME = Now
boomDOUBLE trials
Finished p, Now - beginTIME, CDbl(trials), chosenWorksheet.Range("B2")
p = p + 1
beginTIME = Now
boomUNDECLARED trials
Finished p, Now - beginTIME, CDbl(trials), chosenWorksheet.Range("B2")
p = p + 1
Application.EnableEvents = True
Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True
chosenWorksheet.Calculate
End Sub
Private Sub boomBYTE(numTrials As Long)
Dim a As Byte, b As Byte, c As Byte
Dim i As Long
For i = 1 To numTrials
a = 1
b = 1 + a
c = 1 + b
c = c + 1
Next i
End Sub
Private Sub boomINTEGER(numTrials As Long)
Dim a As Integer, b As Integer, c As Integer
Dim i As Long
For i = 1 To numTrials
a = 1
b = 1 + a
c = 1 + b
c = c + 1
Next i
End Sub
Private Sub boomLONG(numTrials As Long)
Dim a As Long, b As Long, c As Long
Dim i As Long
For i = 1 To numTrials
a = 1
b = 1 + a
c = 1 + b
c = c + 1
Next i
End Sub
Private Sub boomDOUBLE(numTrials As Long)
Dim a As Double, b As Double, c As Double
Dim i As Long
For i = 1 To numTrials
a = 1
b = 1 + a
c = 1 + b
c = c + 1
Next i
End Sub
Private Sub boomUNDECLARED(numTrials As Long)
Dim a As Variant, b As Variant, c As Variant
Dim i As Long
For i = 1 To numTrials
a = 1
b = 1 + a
c = 1 + b
c = c + 1
Next i
End Sub
Private Sub Finished(i As Long, timeUSED As Double, trials As Double, initialCell As Range)
With initialCell.Offset(i, 0)
.Value = trials
.Offset(0, 1).Value = timeUSED
.Offset(0, 2).FormulaR1C1 = "=ROUND(RC[-1]*3600*24,2)"
End With
End Sub
This is a space vs necessity problem.
In some situations it's a necessity to use a long. If you're looping through rows in a large excel file, the variable that holds the row number should be a long.
However, sometimes you will know that an integer can handle your problem and using a long would be a waste of space (memory). Individual variables really don't make much of a difference, but when you start dealing with arrays it can make a big difference.
In VBA7, Integers are 2 bytes and longs are 4 bytes
If you have an array of 1 million numbers between 1 and 10, using an Integer array would take up about 2MB of RAM, compared to roughly 4MB of RAM for a long array.
As others already mentioned, a Long may take twice as much space as an Integer. As others also already mentioned, the high capacity of current computers means you will see no difference in performance whatsoever, unless you are dealing with extra extra extra large amounts of data:
Memory
Considering 1 million values, the difference between using Integers versus Longs would be of 2 bytes for each value, so that is 2 * 1 000 000 / 1,024 / 1024 = less than 2 MB of difference in your RAM, which is likely much less than 1% or even 0.1% of your RAM capacity.
Processing
Considering the benchmark done by PGSystemTester's, you can see a difference of 811 - 745 = 66 seconds between Longs and Integers, when processing 10 billion batches of 4 operations each. Reduce the number to 1 million of operations and we can expect 66 / 10 000 / 4 = less than 2ms of difference in execution time.
I personally use Integers and Longs to help readability of my code, particularly in loops, where an Integer indicates the loop is expected to be small (less than 1000 iterations), whereas a Long tells me the loop is expected to be rather large (more than 1000).
Note this subjective threshold is way below the Integer upper limit, I use Longs just to make the distinction between my own definitions of small and large.
I know it is an old post, but I found it while browsing and I also wanted to share my findings:
after #PGSystemTester's method I got those
and after AJD's
Intel i5-8500T CPU
8gigs of RAM
64bit system and Win10Enterprise 21H1 OS build 19043.2006
while excel is on Version 2108 and Build 14326.20852
I also don't know if it was influencing but I also got Rubberduck Vers. 2.5.2.5906
but it seems like my Integer is faster in both cases
Ty AJD and pgSystemTester. I confirm AJD results for Integer 33 and Long 20. Yet Long is much much faster only because your test program is small enough to fit entirely in the processor cache memory. The fastest ram is L1 cache and that is only 32kB for data and 32kB for program. Testing Byte, Integer and Long for one or several arrays that barely fit into 32kB of L1 data cache will give you totally different or opposite results regarding the speeds.
In my case for the same arrays that totals 120kB for Integers and 240kB for Long i had the same result for Long as for Integer.
That is because changing to Long the same arrays totals double the size compared with Integer arrays, and so, more and more of data had fall outside L1 cache due to the change to Long. To reach data outside L1 cache took much more clocks or time.
Therefore your test is good only as a test yet in real life is misleading as msdn.microsoft recommendation to use Long regardless. Also those who emphasize that ram size is double for Long had not emphasize the consequence for the processor waiting time to reach data outside of L1 cache or even worst outside L2 cache or outside L3 cache. For each outside L1, L2 and L3 the time to reach the data will increase dramatically and this is most important for speed.
To summarize:
if your data fit inside the L1 cache then Long is fastest but
that is only 4k of data times 4Bytes for Long = 16kB (because other programs and OS will populate the rest of 32k of L1 cache),
Byte and Integer will drastically increase the speed for arrays in size
of at least 16kB, because changing to Long will increase size and this
will force more data to reside outside the fastest L1 cache ram.
Try the same test but instead of Dim a As Byte use Dim a() As Byte , example:
Dim a() As Byte, b() As Byte, c() As Byte
ReDim a(7, 24, 60), b(24, 7, 60), c(24, 60, 7)
Dim h As Long, loops As Long: Dim i As Long, j As Long, k As Long ' these i, j, k always As Long
loops=1
For h = 1 To loops
For i = 1 To 6: For j = 0 To 23: For k = 1 To 58
a(i, j, k) = a(i + 1, j, k): b(j, i, k) = b(j, i - 1, k)
c(j, k, i) = a(i - 1, j, k + 1) + b(j, i - 1, k - 1)
Next k: Next j: Next i
For i = 6 To 1 Step -1: For j = 23 To 0 Step -1: For k = 58 To 1 Step -1
a(i, j, k) = a(i + 1, j, k): b(j, i, k) = b(j, i - 1, k)
c(j, k, i) = a(i - 1, j, k + 1) + b(j, i - 1, k - 1)
Next k: Next j: Next i
Next h
First set "loops" to 1 to see how long it takes. Then increase it gradually aiming for several seconds for As Bytes. It will take longer for As Integer, and even longer for As Long...
The size of each of the 3 array is 8x25x61 = 12200 and this is
12200 kB times 3 = 36600 kB for As Byte ,
24400 kB times 3 = 73200 kB for As Integer ,
48800 kB times 3 = 146400 kB for As Long .
Run the same code with Dim a() As Integer, b() As Integer, c() As Integer, then the same with Dim a() As Long, b() As Long, c() As Long and so on.
Now if you increase one dimension 20 times you will expect a 20 increase in duration but it will be much more because now data will fall outside the L2 cache (1MB shared for all 4 cores).
If you increase one dimension 200 times you will expect a 200 increase in duration but it will be again much more because now data will fall outside the L3 cache (6-8MB shared for all 4 cores and the same 8MB for 8 cores or 16MB if ryzen 5800...).
I can't understand why after 20 years or more the L1 cache is only 64kB when it can be at least 16x16=256kB . With 16bits for row address and 16bits for column address you have to read only 32bits and that is one read for 32 bits processor. I suspect that is because perhaps the core still works on 16bits (8 for row + 8 for column address, 8x8=64kB) or worst on only 8bits.
After testing please post your results.
NOTE: My problem was not solved by other similar questions on this as well as other sites. Please have a fair view at my question before judging the same
I am trying to perform a task in which first I have to identify the smallest, 2nd smallest numbers and so on and according to this I have to copy data from one column to another. This will continue until the sum of the copied values becomes grater than or equal to certain value in the sheet (Here row no. for the comparison is given by variable "b"). This will be repeated for 172 different sets which are repeated after every 43 cells.
I have written the following code:
Dim m As Range, k As Double, j As Double, b As Double, lIndex As Double, a As Double
Set m = ActiveSheet.Range("E3:E40")
For i = 1 To 172
j = 1
b = 45 + 43 * (i - 1)
For k = 1 To 38
a = Application.Small(m, j)
lIndex = Application.WorksheetFunction.Match(a, m, 0)
If Cells(b, 7).Value < Cells(b, 1).Value Then
Cells(lIndex, 7).Value = Cells(lIndex, 2).Value
Else
End If
j = j + 1
Next k
Set m = m.Offset(43)
Next i
Now there is an error that pops up saying, Unable to get match property of the worksheet class.
NOTE: I have tried solutions online.
Can there be any other way to do it
OR
Is there something wrong I am doing logically or in the syntax as I am new to excel VBAs and coding itself.
a = Application.Small(m, j) will surely return an Error Code when j is actually bigger that the size of te range m. In your code, the range m = Range("E3:E40") has 38 cells, but j can go as high as 38 * 172.
Then you try to call Match with an error code as the first parameter a. This resuts in run-time error. Note here that Application.Match would result in an error code while WorksheetFunction.Match raises a run-time error.
In all cases, no error should occur in your Match if you had fetched correctly the "kth smallest" element. Without being able to check all of you code, I guess what you wanted here was
a = Application.Small(m, k) ' <--- k, not j
And then no error should occur in *.Match(a, m, 0).
After checking your code:
After getting the smallest value, the next value of j should be a + 1 not j + 1.
Why? because if your smallest value is 4 from (4, 6, 10)
on first loop, j = 1, small will return 4.
on second loop, j = 2, small will still return 4, instead of 6.
I have a VBA Macro that needs to run through a very large Excel file (about 550,000 rows).
I have found that the Macro can only run through about 20,000 lines at a time - any bigger than that and it gives an 'Overflow' error.
My only solution right now is to set j (row number) = 1 to 20,000, run it, then set j from 20,001 to 40,000, run it, then set j from 40,001 to 60,000 etc....all the way to 550,000.
This is a tedious process and I'm wondering is there any way to automate it so that I don't have to keep going back in to change the numbers?
Code looks something like:
Sub ExposedDays()
Dim j As Integer
Dim k As Integer
Dim l As Integer
Dim result As String
For j = 2 To 10000
k = 1
Do While Len(Worksheets("my8").Cells(j, k)) > 0
k = k + 1
Loop
Worksheets("my8").Cells(j, 131) = Worksheets("my8").Cells(j, k - 6)
Worksheets("my8").Cells(j, 132) = Worksheets("my8").Cells(j, k - 5)
.......
The integer data type allocates only enough memory to hold integers -32,768 to 32,767. When you:
dim j as integer
an 'Overflow' error will result when j exceeds 32,767. To solve this problem you need to assign a larger data type.
The Long data type is large enough to hold integers -2,147,483,648 to 2,147,483,647.
If you:
dim j as long
dim k as long
you should be able to avoid this error.
Hope this helps.
I decided to start this question from scratch to clarify the question and goal.
Things to note about my data
I have a spreadsheet containing laptop product data
Each product has a Model and a SKU value
Many models have multiple SKUs associated with them
When more than 1 SKU fits into a model, there is a new row for each SKU. In this instance, each row will have the same value in the model field
Some models may have 4 batteries & 1 charger, others may have 1 battery & 2 chargers, others may have 1 battery & no charger or vice versa... what i'm trying to say is there is no set rule or relation between number of SKUs
There are two types of products, batteries & chargers
All Battery products have SKUs that begin with 'BAT'
All charger products have SKUS that begin with either 'ACA' or 'ACS'
I can easily split the two types of data to help achieve the goal - SKU, model & category data can be placed side by side in columns or in separate worksheets for each type of product (battery or charger)
Example data formatted side by side in same worksheet:
Example data in separate worksheets (sheet1 = batteries, sheet2 = chargers):
Regardless of which method is used, the model field could be positioned anywhere in column A - the model field will not be in an adjacent cell when comparing the two sets of data (as illustrated)
What I am trying to achieve
For each model, I need to have a row of data containing a battery SKU and a charger SKU
There should be a new row for the same model until all combinations are output for that model
There should be a maximum of 2 SKUs in the output for each row. This should always contain 1 battery and 1 charger
Desired Output
It is worth mentioning that this is a very small sample of the data I will be working with, full data set is more than 60k rows and growing so the solution would need to be efficient.
I'm using excel 2007.
I am a complete noob with VBA, I have purchased some plug ins to try and achieve my goal, I have spent 2 days researching and trying various methods to do this but all to no avail.
I thought that I had got close with this answer from Santosh:
https://stackoverflow.com/a/19780188/1018153
Which is what I based my previous question on, but as well as that producing duplicates and matching data between models, I couldn't actually format my data in it's complete form for that script to work for me anyway, so my original question was irrelevant.
The below statement should still work, but I wrote code to try to explain how it would work
Option Explicit 'This ensures typos in variable names are flagged
Sub MakeList()
Dim BatteryList As Range
Dim ChargerList As Range
Dim CurrentModel As String
Dim i As Long
Dim j As Long
Dim k As Long
Dim resultrange As String
'look at the lists - note I am not looking at the type - I'm going to assume
'that we can set the address correctly
'use End(xLdown) to find the last cell - that way we don't need to
'remember to change it when the number of items changes
Set BatteryList = Worksheets("Sheet1").Range("A2", Range("sheet1!B1").End(xlDown))
Set ChargerList = Worksheets("Sheet2").Range("A2", Range("Sheet2!B1").End(xlDown))
'note the use of the Sheet2! and sheet1! in the .End(xlDown) - this is required
'even though we have the Worksheets(" to set the range
i = 2 ' result row
For j = 1 To BatteryList.Rows.Count ' look at each battery row
CurrentModel = BatteryList(j, 1)
For k = 1 To ChargerList.Rows.Count 'then look at each charger row
If ChargerList(k, 1) = CurrentModel Then
'and only write a row if the battery and charger models match
Worksheets("Sheet3").Cells(i, 1) = CurrentModel
Worksheets("Sheet3").Cells(i, 2) = BatteryList(j, 2)
Worksheets("Sheet3").Cells(i, 3) = ChargerList(k, 2)
i = i + 1
End If
Next k
Next j
End Sub
PreviousAnswer
Looking at the code in the question you pointed to, you would need to store the current model, and only add in the possibilities when the model matches. This will result in lots of #N/A! 's when the data is written out, but that should be a minor fix.
at this line:
Do While j <= UBound(c1)
I would insert the code to hold the current model
Dim OnlyThisModel as string
Do While j <= UBound(c1)
OnlyThisModel=c1(j,1)
and in this area
Do While m <= UBound(c4)
out(n, 1) = c1(j, 1)
out(n, 2) = c2(k, 1)
out(n, 3) = c3(l, 1)
out(n, 4) = c4(m, 1)
n = n + 1
m = m + 1
Loop
Check that the model is correct, and don't write if not:
Do While m <= UBound(c4)
if c1(j,1)=OnlyThisModel then
'Only write out data if model matches
out(n, 1) = c1(j, 1)
out(n, 2) = c2(k, 1)
out(n, 3) = c3(l, 1)
out(n, 4) = c4(m, 1)
n = n + 1
end if
'go to next record, regardless of if a combination was written
m = m + 1
Loop