Can't assign cell value to double variable - vba

Good afternoon,
I have a sheet of data that has text and number values. I'm trying to assign the values to an array and check if it's within a certain range. Whenever I get a negative number I get a type mismatch error. I'm not sure how else to convert the data to a double data type. Here's a snippet of the code
Sub()
Dim amount1() as double
Dim h as integer
Dim amtc as range, amtr as range
ReDim amount1(AmtR.Cells.Count)
For Each amtc In AmtR.Cells
h = h + 1
amount1(h) = amtc.Value
end sub
Any ideas to assist a brother?
Thanks

If amtc.Value contains an Error value, VBA cannot implicitly convert it to a Double (or any other type for that matter).
The value of a cell that contains a #VALUE! error will be seen by VBA as being of a special Error type. You can generate these error values yourself for testing, with the CVErr function:
Debug.Print TypeName(CVErr(xlErrValue)) ' prints "Error"
Debug.Print VarType(CVErr(xlErrValue)) = vbError ' prints "True"
You can use the IsError VBA function to determine if a Variant contains an Error value:
If Not IsError(amtc.Value) Then amount1(h) = amtc.Value
But that can still give you a type mismatch if the Variant contains a non-numeric String, so you can use the IsNumeric VBA function to ensure the type conversion will succeed:
If IsNumeric(amtc.Value) Then amount1(h) = amtc.Value
That's still an implicit type conversion though. You can use the CDbl VBA function to explicitly perform the type conversion:
If IsNumeric(amtc.Value) Then amount1(h) = CDbl(amtc.Value)
And now you have code that does what it says, and says what it does.

Good day Matt & Scott,
I've tried both solutions, one thing that's clear is that it's not Numeric. What is baffling me now is that it's only on negative numbers where I can't format the data type, but it's allowing me to convert the negative number to a string and I'm unable to convert the string to a double.
Hope you guys can shed some light...

Related

"VBA.len" is 4 times slower than "len" (?!) [duplicate]

When I run the following macro:
Sub try()
Dim num As Integer
num = 123
MsgBox Len(num)
MsgBox VBA.Len(num)
End Sub
The first Msgbox displays 2 and the second Msgbox displays 3.
If I remove the first line which says Dim num As Integer, both MsgBoxes display 3.
Can anyone explain why?
Len and LenB are not just ordinary functions, they are keywords, and as such are found on the list of VBA keywords (although LenB is only mentioned after you click through to Len).
Mid would be another example of such keyword disguised as function, whereas e.g. Left isn't on the list at all, because that is just an ordinary function.
It has to be a keyword because one of its jobs is to perform the compile-time task of determining the storage size of a variable. E.g. with private user-defined types, an ordinary function could not do that at all:
Private Type foo
a As Long
b As String
End Type
Sub TestLens()
Dim f As foo
MsgBox Len(f) ' OK
MsgBox VBA.Len(f) ' Compile time error
End Sub
The fact that the object browser brings you to VBA.Len when you press Shift+F2 on that Len(f) is both correct and misleading.
The Len(f) here does not actually call the VBA.Len function that determines the string size, it simply cannot do that because it would require coercing a private struct into a Variant. Instead it calculates the size of foo at compile time and substitutes the result as a literal constant into the executable.
In your example the Len(num) calculates the compile-time size of variable num (which is 2) and substitutes the constant 2 into the resulting object code, whereas VBA.Len(num) packs the value of num into a Variant and passes that variant to VBA.Len where it's further converted to string "123" and the length of that is returned.
It has to do with the way that VB stores integers and how the Len() function handles arguments that are not strings.
When a datatype that is not a string is passed to the Len() function, it returns the nominal number of bytes used to store the data (VB uses 2 bytes to store an integer). See the documentation for the Len function.
The Len() function will automatically cast the variant variable (which is created by assigning a value to a variable without declaring it first) as a string. The return isn't changing because the storage allocation changes (although it does; variants require 16 bytes of storage space, minimum). Since the implicitly declared variable is actually a variant type, VB will automatically change its type based on the situation. In this case, Len() expects a string so VB makes the variable a string.
You could use Msgbox Len(Cstr(num)) to cast the integer variable as a string before passing it to the Len function if your intent is to return the number of characters in your integer value.

What VBA variable type to use when reading values from a cell in Excel?

According to this answer one should always use Variant when assigning values in a cell to a variable in the code. Is this correct? I seem to recall reading elsewhere that using Variant indiscriminately is not a good practice.
You can read a cell value into any type you want, VBA will (try to) implicitly convert it to that type for you.
There are dozens of questions on this site involving run-time errors raised from reading cell values into a specific data type - perhaps you've seen this error message before?
Type mismatch
That's the error you get when you try to read a cell containing an error value (e.g. #REF!) into anything other than a Variant.
So if you read a cell value into, say, a Double, everything will work fine as long as you're reading something that VBA can coerce into that data type. The problem is that, well, data is never 100% clean, worksheets do break down, users delete columns and break formulas, lookups fail and the person that wrote the formula didn't bother wrapping it with IFERROR, etc.
That's why you read cell values into a Variant.
That doesn't mean you work with a Variant.
Dim cellValue As Variant
cellValue = someRange.Value
If IsError(cellValue) Then Exit Sub 'bail out before we blow up
Dim workingValue As String
workingValue = CStr(cellValue)
By assigning to another data type, you effectively cast the Variant to that more specific type - here a String. And because you like explicit type conversions, you use VBA's conversion functions to make the conversion explicit - here CStr.
Now, in real code, you probably wouldn't even bother reading it into a Variant - you can use IsError to test the cell value:
If IsError(someRange.Value) Then Exit Sub 'bail out before we blow up
Dim cellValue As String
cellValue = someRange.Value ' or cellValue = CStr(someRange.Value)
The flipside here is that you're accessing the cell twice. Whether or not that's better that reading it into a Variant is for you to decide; performance-wise, it's usually best to avoid accessing ranges as much as possible though.
The value you get from a cell (which is a Range) is a Variant according to the documentation:
Range.Value Property (Excel)
Returns or sets a Variant value that represents the value of the specified range.
Since a Variant can represent different data types, you could loose information if you would assign a cell's value to -- for instance -- a variable of type String.
The mere fact that there is data type information in a Variant already means you lose that type of information. If for instance the original type was numeric and you store it in a String variable, there is no way to know from that string value what the original data type was. You could also lose precision (on Date milliseconds for instance).
Furthermore, a Variant type value cannot always be cast to the data type of your variable, and so you could get a Type mismatch error. In practice this often happens with the Error sub data type.
Only when you know beforehand what the data type is of a certain cell's value, it would be good to define your receiving variable in that data type.
Not strictly answering your question, but thought I'd add this for reference anyway.
With native Excel functions you can usually provide either a range object or a value directly to a function. For example, you can either write =AVERAGE(A1,A2,A3) or =AVERAGE(10,20,30). If you want to do something similar for any user defined functions, you will need to check the type of object passed to your function:
Function test(input As Variant)
Dim var As Variant
If TypeName(input) = "Range" Then
var = input.Value
Else
var = input
End If
You may also want to check for other objects if your function can accept them, but doing this will make your functions behave more like users expect them to.

Why do Len and VBA.Len return different results?

When I run the following macro:
Sub try()
Dim num As Integer
num = 123
MsgBox Len(num)
MsgBox VBA.Len(num)
End Sub
The first Msgbox displays 2 and the second Msgbox displays 3.
If I remove the first line which says Dim num As Integer, both MsgBoxes display 3.
Can anyone explain why?
Len and LenB are not just ordinary functions, they are keywords, and as such are found on the list of VBA keywords (although LenB is only mentioned after you click through to Len).
Mid would be another example of such keyword disguised as function, whereas e.g. Left isn't on the list at all, because that is just an ordinary function.
It has to be a keyword because one of its jobs is to perform the compile-time task of determining the storage size of a variable. E.g. with private user-defined types, an ordinary function could not do that at all:
Private Type foo
a As Long
b As String
End Type
Sub TestLens()
Dim f As foo
MsgBox Len(f) ' OK
MsgBox VBA.Len(f) ' Compile time error
End Sub
The fact that the object browser brings you to VBA.Len when you press Shift+F2 on that Len(f) is both correct and misleading.
The Len(f) here does not actually call the VBA.Len function that determines the string size, it simply cannot do that because it would require coercing a private struct into a Variant. Instead it calculates the size of foo at compile time and substitutes the result as a literal constant into the executable.
In your example the Len(num) calculates the compile-time size of variable num (which is 2) and substitutes the constant 2 into the resulting object code, whereas VBA.Len(num) packs the value of num into a Variant and passes that variant to VBA.Len where it's further converted to string "123" and the length of that is returned.
It has to do with the way that VB stores integers and how the Len() function handles arguments that are not strings.
When a datatype that is not a string is passed to the Len() function, it returns the nominal number of bytes used to store the data (VB uses 2 bytes to store an integer). See the documentation for the Len function.
The Len() function will automatically cast the variant variable (which is created by assigning a value to a variable without declaring it first) as a string. The return isn't changing because the storage allocation changes (although it does; variants require 16 bytes of storage space, minimum). Since the implicitly declared variable is actually a variant type, VB will automatically change its type based on the situation. In this case, Len() expects a string so VB makes the variable a string.
You could use Msgbox Len(Cstr(num)) to cast the integer variable as a string before passing it to the Len function if your intent is to return the number of characters in your integer value.

InvalidCastException Conversion from string "" to type 'Double' is not valid

I'm developing a system that shows the total amount paid of a student from the database. For some students that only have a few transactions the function below gives an error Conversion from string "" to type double... but if a student doesn't have any data entry or the values are zero-to-many the code below encounters it. What should I do? Do I need to adjust the code? Please help.
I have here an Function that gets the total items.
Function Totalpaid() As Double
Dim i As Integer
For i = 0 To ((tbl_receipt.Items.Count) - 1)
Totalpaid = (Totalpaid + tbl_receipt.Items(i).SubItems(3).Text)
Next
End Function
You're trying to add two numbers but one of the operands is not a number. You're assuming that the Text of the subitem will be converted a number and added to the running total. At least one of your subitems is blank though. Maybe you consider that to represent zero but it doesn't. It doesn't represent any number at all so the operation fails.
What you should be doing is validating and converting the subitem contents yourself and then providing the system with two Double values to add. If there is no value then you either ignore that subitem or use zero explicitly. Option Strict On will at least force you to perform the conversion but it's still up to you to provide the validation if there's any chance that the data will not be valid.
To me this looks like a problem with your data rather than the code directly. I would recommend sanitising your data before trying to use it in a calculation, i.e.:
Function Totalpaid() As Double
Dim i As Integer
Dim val as Double
For i = 0 To ((tbl_receipt.Items.Count) - 1)
If Double.TryParse(tbl_receipt.Items(i).SubItems(3).Text, val) Then
Totalpaid = (Totalpaid + val)
End If
Next
Return Totalpaid
End Function
The message Conversion from string "" to type double... is saying that your are trying to convert an empty string to a Double which cannot work.
As a side note, when testing your code I did have to include the line Return Totalpaid, I don't know if this was missed when you where writing out the question.

convert string to double in vb.net

data table contain column named as Fld_primary. this column contain value like 0.00 it is double datatype in mysql table.i want store that datatable value in double variable. am always getting error when i convert to double datatype.
my code
-------
Dim ds5 As dataset1.DTSUMDataTable = TA5.GetData(users)
Dim dttot As Double
dttot = CType(ds5("fld_primary").ToString, Double)
Error
Conversion from string "fld_primary" to type 'Integer' is not valid.
Edited # 3:01 AM with most recent screen caps.
Sometimes I find myself second guessing certain code based on people's answers, but I went ahead and took the time to check if the code that they are all using is even valid:
As you can see that code is a no go, so I used the correct code you see at the bottom there to reference a column.
However, if you wish to get a single cell use the chunk of code below that uses the foreach loop (the rest is my basic setup to show you how it works):
"Y" will equal the value of the datatable cell and you may convert it using the Double.Parse() method:
Dim y = Double.Parse(zDataRow("cat").ToString())
Be careful, if you have multiple rows you will notice that the value of y will change as it makes its way through all the rows.
you can convert it using the Convert class.
Dim dttot As Double = Convert.ToDouble(ds5("fld_primary"))
Your error is actually: ds5 expects an integer as a parameter, so using ds5("fld_primary") is not valid in your code. Perhaps you can try ds5(0)("fld_primary").
After you fixed it, use
dttot = Double.Parse(whatever_string_you_should_put_here)
If you cannot ensure your string must be a valid double, then use Double.TryParse.
You are better off using the 'Double.TryParse' way of converting as this handles any errors better and simply returns a boolean if succesful or not, using 'parse' will throw an exception which isnt anywhere near as elegant.
Dim dub as Double = 0
Double.TryParse("Your String Here", dub)
Try using Double.TryParse(text,value)
Try using this:
For a=0 to yourgridview.rows.count-1
Yourgridview.rows(a).cells(targetcolumnnumber).value=cdbl(Yourgridview.rows(a).cells(targetcolumnnumber).value)
Next