I have a simple row counter.
Total = Sheets("support").Range("A1", Sheets("support").Range("A1").End(xlDown)).Cells.Count
It works perfect until I count more ten thousands rows, because in that case I get this error message:
Overflow
What cause this and how can I count for example more hundred thousands rows?
.Cells is redundant, the .Count member call can be made directly off the Range object returned by the preceding Range.End member call.
That said, it depends what Total is declared as. If it's an Integer, then it only has 16 bits to store a signed integer, which means its upper limit is 32,767 - i.e. 2^15-1.
Dim Total As Integer ' 16-bit
Using a Long integer would give it 32 bits to do the same, making its upper limit 2^31-1, which should be more than enough for most uses.
Dim Total As Long ' 32-bit
If you overflow a Long, VBA7 on 64-bit systems gives you LongLong, a 64-bit signed integer type that won't overflow until 2^63-1 is busted.
Dim Total As LongLong ' 64-bit
You should use the CountLarge property instead of Count, i.e.
.Cells.CountLarge
Related
I'm using an API function which returns a DWORD
Because I want intellisense on the LoWord and HiWord, rather than using a Long:
Declare Sub myAPI(ByRef outVariable As Long)
...as suggested in this list of WinAPI -> VBA datatype conversions, I'm using a type:
Public Type DWORD 'same size as Long, but intellisense on members is nice
'#Ignore IntegerDataType
LoWord As Integer
'#Ignore IntegerDataType
HiWord As Integer
End Type
Declare Sub myAPI(ByRef outVariable As DWORD)
However RubberDuck's IntegerDataType inspection reminded me that on 32 bit systems VBA converts 2-byte Integers to 4-byte Longs internally, so I'm wondering whether my DWORD declaration is really 4 consecutive bytes as expected, or 8.
I'm not familiar enough with pointers and bits & bytes to picture in my head what's going on, but I imagine the API somehow knows to fill only the lower half of each part, as I've been getting the results I expect (I think) from the API.
Your user defined type is 4 bytes is size, because Integer is 2 bytes in size.
You can check this for yourself:
Dim dw as DWORD
Dim size as Integer
size = LenB(dw)
My code tries to multiply 12303 by 174596.
Any decent calculator is capable of providing an answer to this, so why do I get an OverflowException? It also happens when I execute it directly in the Immediate Window.
The code is meant to determine the position of a certain value in a binary file. The file itself is 7 Gb in size.
Is there any way to solve this?
Dim position As Long = hisFileHeader.StreamStartDataPosition +
(TSIdx * hisFileHeader.StreamDataBlockSize)
tsidx has a value of 12303 and StreamDataBlockSize has a value of 174596
I'm guessing that tsidx and StreamDataBlockSize are Integer types. The largest number an Integer type can hold is 2,147,483,647. The multiplication in brackets is then done expecting an integer result, but the answer is out of the range of Integer types. Change your code to ..
Dim position As Long = hisFileHeader.StreamStartDataPosition + (CLng(TSIdx) * hisFileHeader.StreamDataBlockSize)
and the multiplication will be done with the expectation of a Long type.
How would I go about storing a very large number in number format and not scientific.
Please bear in mind that the number I will be storing is too big for the Long data type.
I've set it as a String.
I have a userform with a command button and a textbox.
Below is the sample code:
Private Sub Cmd_Click()
Dim bits As Integer
Dim out As String
bits = 64
out = 2 ^ (bits - 1)
Txt_Output.Value = out
End Sub
The above will return: 9.22337203685478E+18.
But I want 9223372036854775807.
Can anyone explain how to avoid this?
Thanks in advance.
P.S. I'm hoping to avoid having to use an array.
You can achieve that specific calculation using Decimal data types and a modification to the calculation routine:
Private Sub Cmd_Click()
Dim bits As Integer
Dim out As Variant
Dim i As Long
bits = 64
out = CDec(1)
For i = 1 to bits - 1
out = out * 2
Next
Txt_Output.Value = out
End Sub
By forcing out to be a Variant/Decimal, the calculation does not lose precision as it is being calculated. However some things, such as CDec(2) ^ CDec(63) would still lose precision as the calculation would be done using an intermediate Double precision, so you will need to be very careful as to what calculations you do.
This might give you clues as to how to generalise that method to achieve what you need.
If you have 64-bit Excel, you can use the LongLong data type.
Using the immediate window to do some debugging, I came across the following which I have simplified for the purpose of this question:
running this command:
?20000*2
produces an 'overflow' error. Let's assume this is because we haven't declared a data type, and VBE has assumed Integer - the result falls outside the boundaries of a signed integer and therefore an overflow occurs.
However if I run:
?39999+1
I get 40000 as expected.
Is this because I've initially started with a Long instead of an Integer (i.e. 20,000 vs 39,999)? And therefore memory is allocated based on the initial input data rather than the calculation result?
That's correct. VBA will take the largest of the input components and allocate memory for the results. Since both of the components in the first example are Int, that's all you get.
You can use a type declaration character to force the VBE to treat a number as a certain data type
?20000&*2
40000
?20000*2&
40000
In both those examples, the & (Long type declaration character) forces the memory allocation to a Long. It doesn't matter if it's the first component or a later one. I think there are some operations that get forced into particular data types. Exponentiation is one of them.
?2^2^2^2^2^2
4294967296
?typename(2^2^2^2^2^2)
Double
Even though all the components are Integers, the results is a Double - even when it doesn't have to be
?typename(2^2)
Double
This implicit typing isn't limited to the Immediate Window. The same overflows can occur in your code:
Sub foo()
Dim x As Long
x = 20000 * 2 'Overflow error
End Sub
Also, when a String is implicitly cast to a numeric type, it's cast as a Double:
?TypeName("123" + 6)
Double
I'm doing basic random number generation using this Shared Function:
Public Shared Function RandomNumber(ByVal MaxNumber As Integer, Optional ByVal MinNumber As Integer = 0) As Integer
'initialize random number generator
Dim r As New Random(Date.Now.Ticks And &HFFFF)
If MinNumber > MaxNumber Then
Dim t As Integer = MinNumber
MinNumber = MaxNumber
MaxNumber = t
End If
Return r.Next(MinNumber, MaxNumber)
End Function
Called like this: dim x as integer = Random(2100000000)
Very simple, and the seed value comes straight from a MS example.
HERE'S THE PROBLEM: I'm getting duplicate numbers on occasion, but always created at times that are usually at least 5 or 10 minutes apart. I can see if I was calling the function multiple times per second or millisecond, because that'd kind of "breaks" the seed. But these are showing up at extended time spans. What else could be causing this?
Duplicate seed issue?
It might be better defining r as static so that it is initialised once when first invoked. Refer to this answer Random integer in VB.NET
The Random constructor takes an Integer as its parameter which is 32-bits. As spencer7593 said, with only 16 bits, you're repeating the sequence every 6.5ms. Try:
Dim r As New Random(Date.Now.Ticks And &HFFFFFFFF)
However, this will do the same thing:
Dim r As New Random()
Better yet, don't create a new Random object each time:
Private Static r As New Random()
Public Shared Function RandomNumber(MaxNumber As Integer, Optional MinNumber As Integer = 0) As Integer
...
Return r.Next(MinNumber, MaxNumber)
End Function
Q: What else could be causing this?
A: It could be happening purely by random. Random numbers are just that: random. At any point in time, whether its seconds or hours away from another point in time, its just as likely for a number to appear as any other number. There is no guarantee that a number won't be repeated.
On the other hand, it looks like your seed value is only on the order of 16 bits. And that's like a total of 65,536 possibilities. There's 10,000 ticks in a millisecond, so ever 6.5 milliseconds you have the possibility of reusing the same seed.
It's not at all clear whether the VB Random is using some other kind of entropy beyond that seed or not. (But gathering entropy for inclusion would slow down the initialization, so it may not be, as a performance consideration.)
According the docs, creating two Random objects using the same seed value results in Random objects that create duplicate sequences of unique numbers.
http://msdn.microsoft.com/en-us/library/ctssatww.aspx
I think that answers the question why it is happening.
I guess the next question is why do you need to instantiate a new Random object? If you need multiple objects, then instantiating several of them, but making sure you are using a different seed value for each one would be one approach.
But before you go there, I recommend you consider using just one Random. Calls to get random numbers can be serviced from an existing Random, rather than creating a new one every time you need a random number.
Try it another way:
Public Function RandomNumber2(ByVal MaxNumber As Integer, Optional ByVal MinNumber As Integer = 0) As Integer
' Initialize the random-number generator.
Randomize()
' Generate random value between MaxNumber and MinNumber.
Return CInt(Int((MaxNumber * Rnd()) + MinNumber))
End Function
See Randomize Function (Visual Basic) for more details. Hope this helps.