Sub CommandButton1_Click()
Dim x As Integer
x = 6
Range("A1").Value = x
End Sub
This means that you assign X as Integer, and you say the x equals to 6.
And then you put the value x(=6) in cell "A1".
Sub CommandButton1_Click()
Dim x As Double
x = 6
Range("A1").Value = x
End Sub
But why does the second one work also?
TL;DR: Type conversions.
Range.Value is a Variant, which is a special data type that stores a pointer to a value, along with a description of the data type: that's how a cell can hold a Double, a String, a Boolean, or an Error value.
Anything deeper than that is irrelevant to the question at hand.
Integer is a 16-bit signed integer type that can easily fit into a Double, which is much larger than 16 bits. If you followed the .Value assignment with this:
Debug.Print TypeName(Range("A1").Value)
You would get Double in the debug output.
Somewhere in the implementation of the Range.Value property, a validation of the supplied value is performed, and if the value is acceptable, it's stored internally in the appropriate data type. If the value isn't of an acceptable data type, error 1004 is thrown. Integer being a fine numeric value, all is good.
The exact same thing happens in the second snippet: Double being a fine numeric value, all is good. And since any numeric value taken from a cell is a Variant/Double, we can reasonably infer that somewhere in the internal guts of Range, numeric values are stored as Double - although, that could very well just be an implementation detail of how the getter of the Range.Value property is implemented.
VBA was designed to work with a specific set of data types, and the type libraries of VBA host applications (e.g. Excel) were designed to accept these data types. Hence, you would have to work pretty hard to give Range.Value a value it can't deal with, using VBA code.
But before the value even gets to the Range.Value property, an implicit type conversion has aready occurred in the second snippet.
Dim x As Integer
x = 6
Here 6 is an integer literal. When VBA executes the x = 6 instruction, that 6 already has a data type - and that data type is Integer.
Dim x As Double
x = 6
Here 6 is also an integer literal, but it's assigned to a Double, which isn't the same type: an implicit type conversion occurs, and x happily takes an Integer value - because the conversion is widening.
Now consider:
Dim x As Double
x = 6#
Here 6# uses a type hint. Debug.Print TypeName(6#) prints Double: that 6# is a Double literal - no type conversion occurs here. But it's ugly.
Dim x As Double
x = CDbl(6)
Now the widening type conversion is explicit.
When an implicit conversion is narrowing instead, and the value can't fit into the needed data type...
Dim x As Integer
x = 32768 '<~ that's a Long integer literal: doesn't fit the 16 bits of an Integer
...then runtime error 6 ("Overflow") is thrown. Since every VBA numeric data type can safely be converted to a Double, every numeric value that can be supplied by VBA code, can be assigned to Range.Value.
Internally Excel does not use an Integer. Cells are one of four types:
Double precision floating point (all numbers including integers,
currency, dates times etc)
String
Boolean
Error
Note this means all numbers are doubles.
See these references:
Data types used by Excel - "All worksheet numbers in Excel are stored as doubles"
Excel VBA internal data format and memory storage
Related
As I am new to Excel VBA, how to classify a variable that has both letters and numbers? Like for instance "f0rmym8" do I Dim As Long or Dim As String or something else?
Thanks!
As per the documentation the string type variable is the only one capable of holding characters. (with the exclusion of variant, being able to hold an array of strings). All other data types are practically purely numeric, including Date and Boolean in VBA, with Date being effectively a Double formatted as a date, and a Boolean being -1 (true) or 0 (false).
In summary whenever you need to hold any kind of ASCII character that is not numeric, you will need to use the string data type, regardless of whether there are also numeric characters present. A numeric data type is always incapable of holding any type of text.
A String can hold any group of characters, consider:
Sub catscradle()
Dim Letters As String, MixedStuff As String, JustNumerals As String
Dim NothingAtAll As String
Letters = "ABC"
MixedStuff = "1A2B3C"
JustNumerals = "0076941"
NothingAtAll = ""
End Sub
Weird Math Error in VBA for Excel
Hi all, would love feedback on unsusual error I'm getting.
Very strange. I have a simple formula that works great if I
only use it in a normal sheet cell and copy it down by columns,
but if I try to do a simple iteration in vba code to perform the same function I get the wrong values.
Description : A number is squared, then divided by another value
between 0.99 to 1.99, next the modulus is taken and then
the number is squared again and the whole formula repeated.
If I copy the formula statement down column wise it calcs fine,
including reasonable decimal accuracy.
There are four inputs ;
base value (inputx)
decx = divisor
mod value
The first formula placed at (E2) looks like ; =MOD(((B2^2)/$B$3),$B$4)
In (E3) this statement is placed ; =MOD(((E2^2)/$B$3),$B$4)
Then this exact same statement is copied down, columnwise to the next 98 cells.
All great, no problem. It seems accurate value wise, right to decimal
precision, with values past the decimal point showing in all column cells.
Some sample input values for testing ;
INPUTX --> 231
DECX 1.010101
MOD 400
LOOPTIMES 100
But when I try to implement this is Excel VBA code (Excel 2007)
I often get the wrong values and absolutely no values past the decimal point ever show.
Have tried using all kinds of different data types ; single, double, variant, etc... but all values returned by the VBA function I made always returns
whole numbers, and is often wrong and certainly does not agree with the
values returned by the simple column based statements.
Have tried to find ways around this or to fix this, came across "CDEC", tried
this and nothing changed. Totally stumped and would love some insight into
if this can be fixed so that the function loop returns the same values with
same kind of decimal precision as the column based statements and
would greatly appreciate feedback on how mthis can be done.
Am including my sample code below ;
Public Function SQRD(inputx As Variant, looptime As Variant, decx As Variant) As Variant
Application.Volatile
Dim Count As Integer
SQRD = CDec(inputx)
'Dim decx As variant
Count = 1
For Count = 1 To looptime
SQRD = CDec(SQRD ^ 2) '+ looptime
SQRD = CDec(SQRD Mod 400 / decx)
Next Count
End Function
I will only address your use of the VBA Mod operator. It is NOT equivalent to the Excel MOD function. In particular, the VBA Mod operator will round floating point numbers to integers before performing the operation.
To use a VBA function that mimics the Excel MOD function, use something like:
Function xlMOD(a As Double, b As Double) As Double
xlMOD = a - (b * (a \ b))
End Function
EDIT
There seems to be a bug in VBA (or a documentation error). In the formula above, the \ operator is supposed to be the integer division operator. It does return an integer result. However, it does not truncate, rather it rounds. Actually, what it may be doing, is performing VBA type rounding on the number and divisor, before returning the result.
Therefore, a proper vba function to mimic the Excel MOD function would be:
Function xlMOD(a As Double, b As Double) As Double
xlMOD = a - Int(a / b) * b
End Function
Lots amiss with your code. No need for looping as far as I can see, and you're dividing after the mod not before
This seems to do the trick
Public Function NuFunc(InputX As Variant, DecX As Variant) As Variant
NuFunc = ((InputX ^ 2) / DecX) Mod 400
End Function
I have a list (strongly simplified):
Quantity Price Volume
20 10
50 5
40 #N/A #N/A
20 #N/A #N/A
10 30
I have used the following lines of code to calculate volume:
For i = 1 to 5
Cells(i,3).Value = Cells(i,2).Value * Cells(i,1).Value
Next i
I get the type error message when I calculate the above, which I believe has to do with the "#N/A #N/A" error messages I am getting. Any suggestions on how I could circumvent this problem. Like for example, simply skipping the calculation if one the cells equal #N/A #N/A
The * multiplication operator expects operands it can work with, i.e. numeric values.
[ActiveSheet.]Cells(i, 3).Value returns a Variant that will wrap a String given a string cell value, a Date given a date value, a Double given a numeric value... and an Error given an error value.
While VBA is generally very permissive with types and merrily converts a Date to its Double representation, it can't implicitly convert the Variant/Error subtype to anything.
So any time you read a cell's value without checking if it contains an error, you're making an assumption about its data type, and when that assumption is wrong, a type mismatch error occurs.
The solution is to verify whether the Variant subtype involved is an Error value - the IsError function is perfect for this.
Pull the values into local variables, then use IsError to conditionally multiply the operands:
Dim lhs As Variant
lhs = ActiveSheet.Cells(i,2).Value
Dim rhs As Variant
rhs = ActiveSheet.Cells(i,1).Value
If Not IsError(lhs) And Not IsError(rhs) Then
ActiveSheet.Cells(i,3).Value = lhs * rhs
Else
'one of the operands is an error; can't compute...
End If
I am trying to convert 14 bit hex numbers to decimal.
I have this VBA code.
Option Explicit
Public Function HexadecimalToDecimal(HexValue As String) As Double
Dim ModifiedHexValue As String
ModifiedHexValue = Replace(HexValue, "0x", "&H")
HexadecimalToDecimal = CDec(ModifiedHexValue)
End Function
With numbers like this to convert to decimal
0x047B1142591E80
0x044A81325A1E80
0x047B7542591E80
I keep getting random results across large amounts of data. Sometimes spot on other times the numbers are off by 6 or 2.
Try changing the return type of the function from Double to Variant. Double has only about 15 decimal digits of precision, so can't, for example, capture the value 1261213964639872 (which has 16 digits) exactly. The closest it can get is 1261213964639870. By changing the return type to Variant, the full precision returned by CDec will be preserved. You can't use a Decimal return type, because VBA for some reason does not support this.
The problem isn't with VBA. Excel cells can only hold 15 digits in number format. So the "number" 1234567891234567 will always display 1234567891234560. This can be avoided by converting items to text AND/OR changing the cell format to text.
But this doesn't always work.
The only surefire way to make sure it will retain all digits is to append something onto the string that isn't a number.
This code will append an apostrophe before the number, but return the entire string.
Public Function HexadecimalToDecimal(HexValue As String) As String
Dim ModifiedHexValue As String
ModifiedHexValue = Replace(HexValue, "0x", "&H")
HexadecimalToDecimal = "'" & CDec(ModifiedHexValue)
End Function
Unfortunately, not a perfect solution.
I'm having problem making the match() work in excel VBA. The code is:
x = Application.Match("Sep 2008", Range("F1:F1"), 0)
The value in cell F1 is 9/1/2008.
Even if I changed Sep 2008 to 9/1/2008, it still doesn't return any value.
Any idea how to fix it?
The reason why Even if I changed Sep 2008 to 9/1/2008, it still doesn't return any value.
Is because when there is a Date in excel, Excel automatically converts that date to a numeric value, What you really want to search for is:
39692
This number is the number of days between 9/1/2008 and excel default of 1/1/1900
every date in excel is stored with a value like this. So the easiest way to handle this would be to convert what you see as a date to what excel sees as a date using CDate().
This by itself will give you an unuseful error that vba can't get the property.
That is because the Lookup_value can be a value (number, text, or logical value) or a cell reference to a number, text, or logical value. Not a date so simply convert the now date value to a number to search for the matching number in the list using CLng()
Give this a shot it will also be much faster then using the Find alternative:
x = WorksheetFunction.Match(CLng(CDate("Sep 2008")), Range("F1:F1"), 0)
This should give you the result expected
To handle when no match is found try this Sub:
Sub MatchDate()
Dim myvalue As Double
Dim LastRow As Long
LastRow = Cells(Rows.Count, "F").End(xlUp)
On Error GoTo NotFound
myvalue = WorksheetFunction.Match(CLng(CDate("Sep 2008")), Range("F1:F" & LastRow), 0)
MsgBox (myvalue)
End
NotFound:
MsgBox ("No Match Was Found")
End
End:
End Sub
Your best bet is to use .Find(). This will return a range if found or nothing if not.
Set x = Range("F1:F1").Find(CDate("Sept 2008"), , , xlWhole)
If you wanted the column number:
x = Range("F1:F1").Find(CDate("Sept 2008"), , , xlWhole).Column
With capture of not found
Sub test()
Dim y As Date, x As Variant, c As Long
y = CDate("Sep 2008")
Set x = Range("1:1").Find(y, , , xlWhole)
If Not x Is Nothing Then
c = x.Column '<~~found
Else
Exit Sub 'not found
End If
End Sub
Bottom line:
use WorksheetFunction.Match(CDbl(date), range, 0)
Alternatively, use a Date cell's Value2 property (which will also be a Double) instead of Value for the search key.
CLng suggested in other answers would discard the time part of the date.
The same problem exists for the Currency data type but you can't use CDbl for it (see below for options).
Range.Value2 Property (Excel) article suggests that Date and Currency types are "special" in that they have an "internal representation" that's in stark contrast with displayed value. Indeed:
Date is internally represented as IEEE 64-bit (8-byte) floating-point numbers where the integer part is the date and fractional part is the time
Currency is also 8-byte but is treated as a fixed-point number with 4 fractional digits (an integer scaled by 10'000)
Apparently, Match compares these internal values for performance reasons. So, we must ensure that they, rather than the readable representations, match exactly.
Since Date is already floating-point internally, CDbl(date) doesn't actually change the data.
For the Currency type, CDbl does change data, so it's out of question. So either
use the exact representation of the key (to 4 fractional digits) this way or another if you require exact match, or
make the cells in the range actually be formulas with Round) if the value to compare with comes from elsewhere and/or you only require equality to 2 fractional digits
This way it works using this method:
Nbr,L, C as Integer
Datedeb as date
nbr = WorksheetFunction.Match(CLng(CDate(Datedeb)), Range(Cells(L, C), Cells(L + 100, C)), 0)
I think I can safely assume that the value in F1 is a date. In you code "Sep 2008" is a string. You will never be able to get a successful match as long as your datatypes are inconsistent.
If you are looking for a date, then make sure that the first parameter is a date.
Dim dSearchSDate As Date
dSearchSDate = "01/Sept/2008"
x = Application.Match(dSearchSDate, Range("F1:F1"), 0)
Here is another possible approach.
Sub temp()
Dim x
Dim dSearchSDate As Date
dSearchSDate = "01/Sept/2008"
If ThisWorkbook.Worksheets(1).Range("F1:F1").Value = dSearchSDate Then
Debug.Print "Found it!"
Else
Debug.Print "Doh!!"
End If
End Sub
I know this post is old, but I had the same issue, and did find the answer.
To make it work, you first need to make VBA see the same data formatting as it appears in your excel spreadsheet :
YourVar = Format("YourDate","mmm-yyyy")
YourResult = Application.match(Clng(Cdate(YourVar)), YourRange, 0)
Regards
Gilles