IsNumeric returns true for strings containing a D character - vb.net

I had a strange error in a VB6 app this morning and it all stems from the fact that IsNumeric is not working as I expected. Can someone shed some light on why? To me this seems like a bug.
This code displays 4.15877E+62 in a message box:
Dim strMessage As String
strMessage = "0415877D57"
If IsNumeric(strMessage) Then
MsgBox CDbl(strMessage)
Else
MsgBox "not numeric"
End If
I am guessing that the runtime engine is incorrectly thinking that the D is in fact an E?
I think this is a bug though as the exact same code in VB.NET outputs not numeric
Is this a known issue with IsNumeric?

If you check the VB6 docs:
Note Floating-point values can be expressed as mmmEeee or mmmDeee, in which mmm is the mantissa and eee is the exponent (a power of 10). The highest positive value of a Single data type is 3.402823E+38, or 3.4 times 10 to the 38th power; the highest positive value of a Double data type is 1.79769313486232D+308, or about 1.8 times 10 to the 308th power. Using D to separate the mantissa and exponent in a numeric literal causes the value to be treated as a Double data type. Likewise, using E in the same fashion treats the value as a Single data type.

I've been using my own IsNumber function for a long time exactly because of this situation. IsNumeric can also return true for certain money symbols, like this: IsNumeric("$34.20").
My IsNumber function looks like this:
Public Function IsNumber(ByVal Data As String) As Boolean
If Data = "" Then
IsNumber = False
Exit Function
End If
IsNumber = IsNumeric(Data & "e0")
End Function
The idea here is... if there is already an e or d in the data, adding another will cause the data to NOT be numeric using the IsNumeric check. You can easily change this function to only allow for integers by replacing "e0" with ".0e0". Want just positive integers? then use this: IsNumeric("-" & Data & ".0e0")
The only downside of this method is that an empty string normally is not numeric, but when you append "e0" to it, it becomes numeric so you need to add a check for that, like I did in my code.

I suggest making a custom validator. Do you want to allow 0-9 only? What about negatives? Commas? I never cared for Microsoft's implementation, but I understand it.

Related

How to get TrimEnd() to stop at a specific character

I have a series of percentage values saved in a database that look something like this:
Percentage
_____________
100.00000
50.00000
74.02500
When I display the values to the screen, I'd like to trim unnecessary zeroes from the end of the string along with the decimal point so the above examples become:
Percentage
_____________
100
50
74.025
I'm currently using the following code:
displayVal = rawVal.TrimEnd({"0"c, "."c})
but this code continues to trim after the decimal if there are additional zeroes. I also tried:
displayVal = rawVal.TrimEnd(New String({"0", "."}))
which almost works. It just leaves the decimal point.
Is there a way to do what I want using TrimEnd() or do I need to switch to regex?
As Tim already mentioned in the comments, if the data type in the DB is already some numerical type, it would be best to keep it in that type and then use the appropriate numeric formatting when converting it to a string for output. If, however, the input data is already a string, then that's not an option. In that cast, the simplest option is to just do two trims in series, like this:
Private Function RemoveUnecessaryZeros(input As String) As String
Return input.TrimEnd("0"c).TrimEnd("."c)
End Function
However, that doesn't give you a lot of flexibility, it doesn't remove preceding zeros, and it does nothing to reformat the string using the current culture. If that matters, you could instead parse the value into a numeric type and then use the desired string formatting options to re-output it to a string. For instance:
Private Function RemoveUnecessaryZeros(input As String) As String
Dim result As Double
If Double.TryParse(input, result) Then
Return result.ToString()
Else
Return input
End If
End Function
However, when you do it that way, you may potentially lose precision along the way, depending on the input numbers and the data type you choose to parse it with. If you need more control over the parsing/reformatting and you want to keep it purely in strings so no precision is lost, then you may want to consider using regex. For instance:
Private Function RemoveUnecessaryZeros(input As String) As String
Dim m As Match = Regex.Match(input, "[1-9]\d*(\.([1-9]|0+[1-9])+)?")
If m.Success Then
Return m.Value
Else
Return input
End If
End Function

LenB Function is Not Working (VBA)

I am currently working on an Access 2010 application which have textboxes that accepts inputs in Japanese characters (KANJI). As soon as the focus (On Exit event) is lost in the textbox, it checks if all the characters are in double-bytes otherwise it will generate a warning message. You may refer to the code below:
If LenB(StrConv(Trim(strJapanese), vbFromUnicode)) <> Len(Trim(strJapanese)) * 2 Then
'Inform the user to input double-byte Japanese characters
End If
The problem is that even if I input double-byte characters in the textbox, the condition above is satisfied. This means that the byte-length of the string is equal to its length.
Please note that using this Access tool in my colleague's computer and inputting Japanese characters will not satisfy the condition above - which is the expected behavior. Is this a problem with my environment? We are using the same operating system and same MS Office version so I don't understand why we're not having the same results.
I would appreciate any help regarding this matter. Thanks!
I don't know about LenB, but if you just want to check whether a string contains kanji, that's much easier to do simply by looping through each character in the string, and checking AscW(char). That will return a number representing the Unicode value of the character, and all kanji are contained between &H4E00 (一) and &H9FAF (龯). The only exception to this is the kanji iteration marker 々, which is Unicode &H3005.
Note: AscW returns positive numbers from 1 to 32767 for &H1 to &H7FFF, and it returns negative numbers from -32768 to -1 for &H8000 to &HFFFF. This split happens right in the middle of the kanji Unicode block, so you can't simply use a switch statement "Case &H4E00 to &H9FAF"...you have to split it apart as "Case &H4E00 To &H7FFF, &H8000 To &H9FAF".
Function containsOnlyKanji(str As String) As Boolean
If Len(str) = 0 Then Exit Function 'empty strings don't have any kanji so return false
Dim i As Long
For i = 1 To Len(str)
Select Case AscW(Mid(str, i, 1))
Case &H4E00 To &H7FFF, &H8000 To &H9FAF, &H3005: 'do nothing, these are kanji
Case Else: Exit Function 'non kanji! don't have to check the rest
End Select
Next i
containsOnlyKanji = True 'if we've made it this far, the user must have entered only kanji
End Function
I know this doesn't directly answer your question, and I hate when people post answers that don't directly answer the question, but hopefully this will do what you need it to. (Also, if you want to include katakana and hiragana, those are contained in Unicode blocks &H30A1 to &H30FE and &H3040 to &H309F, respectively.)

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.

Instr condition doesn't work with 2 or more conditions

I have the following INSTR condition in Excel VBA which doesn't work (all the time)
STR_TEXT="SKU1234 $100/10' $200/20'" ' < example string
IF INSTR(STR_TEXT,"/10'") AND INSTR(STR_TEXT,"/20'") THEN
' code
ELSE
' code
END IF
For some obscure reason, it seems like it cannot check for both conditions so the first IF, even if both condition match, doesn't seem to work and goes to ELSE.
The following does work:
STR_TEXT="SKU1234 $100/10' $200/20'" ' < example string
IF INSTR(STR_TEXT,"/10'") THEN
IF INSTR(STR_TEXT,"/20'") THEN
' code
END IF
ELSE
' code
END IF
As you can see, if I separate the conditions on the first IF, it works.
But I would prefer to have both conditions in same IF, as code is 'cleaner'.
Anyone knows why and/or how to fix it without having to put an IF inside another IF ?
The other answers point out the most important thing, which is that InStr actually returns the numeric position of one string in another (or 0 if the desired string isn't found). As they say, you should be testing the condition <result> > 0 in your If statement. I'll just address what the reason is behind your observation that your test "doesn't work (all the time)". It's a nice chance to revel in some ancient I-<3-BASIC awesomeness.
What's going on is that, in this case (see edit at the bottom for more) VBA's And operator (and Or, etc.) is actually a bitwise operator, not a logical one. That is, if you pass it two integer operands, it will do a bit-by-bit And, and return back the resulting integer. For example 42 And 99 evaluates to 34, because (in binary) 0101010 And 1100011 is 0100010.
Now, normally, if you use VBA Boolean values, And works like a logical operator. This is because in VBA, the constant True is equal to the numeric -1, and False is equal to the numeric zero. Because VBA represents -1 as a binary number with all bits set, and zero as a binary number with all bits cleared, you can see that binary operations become equivalent to logical operations. -1 And <something> always equals the same <something>. But if you're just passing any old numbers to And, you'll be getting back a number, and it won't always be a numeric value that is equal to the constants True or False.
Consider a simple example (typed in the Immediate window):
x="abc"
?Instr(x,"a")
1
?Instr(x,"b")
2
?Instr(x,"c")
3
?(Instr(x,"a") and Instr(x, "b"))
0
?(Instr(x,"a") and Instr(x, "c"))
1
Now recall that VBA's If statement treats any non-zero numeric argument as being the same as True, and a zero numeric argument as being the same as False. When you put all this together, you'll find that a statement of your example form:
IF INSTR(STR_TEXT,"/10'") AND INSTR(STR_TEXT,"/20'") THEN
will sometimes pick the first condition and sometimes the second, depending on just what is in the searched string. That's because sometimes the bitwise And operation will return zero and sometimes it will return non-zero. The exact result will depend on the exact positions of the found strings, and this clearly isn't what you'd expect. So that's why the advice you've already gotten matters in the details.
EDIT: As pointed out by Hugh Allen in this comment:
Does the VBA "And" operator evaluate the second argument when the first is false?
VBA's And operator does actually return Boolean values of both of it's operands are Boolean. So saying that it's a bitwise operator is not strictly correct. It's correct for this problem though. Also, the fact that it can act as a bitwise operator does mean that it can't act like a "normal", purely logical, operator. For example, because it must evaluate both operands in order to determine if they are numbers or not, it can't short-circuit.
EXMAPLE:
if instr(str_Text,"/10'") > 0 AND instr(str_text,"/20'") > 0 then
What Tim is saying is that the instr function returns the position in the string of the first instance of the string being searched for..
so in your example: 13 is being returned for instr(str_Text,"/10').
When VBA reads your version instr(str_text,"/10;") (without the >0) then it sees that the result is not 1 (which means true) so it always hits the else)
Instr() return a numeric result: if it's >0 then the tested string contains the string being searched for.
Sub Test()
Dim str_text As String
str_text = "SKU1234 $100/10' $200/20'" ' < example string
If InStr(str_text, "/10'") > 0 And InStr(str_text, "/20'") > 0 Then
MsgBox "Both"
Else
MsgBox "Only one, or none"
End If
End Sub
The INSTR function will return the index of the sub-string you are trying to find in a string.
So the following will give a numeric value instead of a boolean value:
INSTR(STR_TEXT,"/10'")
To fix this, use the following which will give a boolean answer which is required by if condition:
INSTR(STR_TEXT,"/10'") > 0

adding variables numical values (newb question)

Yesterday i had a look at how to set values of variables from nummbers stored in external txt files
the variables then needed to be added up so i used trial and error first
((XVAL) + (NEWVAL))
assuming that XVAL was set to 10 and NEWVAL was set to 20 i expected to get the answer of thirty but waqs presented with the new value of 10 20
VB.net pysicaly added the two values together but i wanted the mathematical product of the two which is ((10) + (20)) = 30
yep its a newb question could anyone explain how to achieve what im affter
XVAL and NEWVAL are strings, so they are simply being concatenated together. You need to convert them to integers, so that VB.NET will treat them as such. To do this, use the Int32.Parse() method.
Dim intXVAL As Integer = Int32.Parse(XVAL)
Dim intNEWVAL as Integer = Int32.Parse(NEWVAL)
Dim result = intXVAL + intNEWVAL
You want to cast them to a number first.
Try CDbl.
See http://msdn.microsoft.com/en-us/library/Aa263426 for more.
edit: Oops, thought you were talking about VBA.
Try using Double.Parse(YOURVALUE) if you're talking about VB.NET.
Have you tried the Val() function?
Val(XVAL) + Val(NEWVAL)
The + operator in VB.NET (for backwards-compatibility reasons) means both add and concatenate depending on the types of the variables it is being used with. With two numeric types (Integer, Single, Double, etc.), it adds the values together as you would expect. However, with String types, it concatenates the two strings.
Presumably, then, your XVAL and NEWVAL variables are String types because they're being read out of a text file, which is causing VB.NET to concatenate them into a new string instead of add them together. To get the behavior you're expecting, you need to convert them to numeric types.
Some of the other answers suggest casting simply casting the string values to numeric types (CInt, CSng, CDbl, etc.), but this may not work as expected if the value contained by your string cannot be converted to number. The Int32.Parse method will throw an exception if the value held by your string cannot be represented as a number. This is especially important to keep in mind if you're reading values from a text file that are not guaranteed to adhere to any particular constraints.
Instead, you probably want to use something like Int32.TryParse, which returns a Boolean value indicating whether or not the conversion succeeded and will not throw an exception.
As you are reading from a text file I assume that you are reading your values out as strings, so when you do this:
((XVAL) + (NEWVAL))
It is effectively concatenating the two strings together. In order to get the mathematical product of the two values these need to be int/integers which is the number type.
There are a number of ways you can do this, but in essence you have to 'cast' the strings to ints and then do your calculation.
So in vb.net it would be something like this (pseudo code):
Dim xval As String = "10"
Dim newval As String = "20"
Dim x As Integer = Int32.Parse(xval)
Dim n As Integer = Int32.Parse(newval)
Dim prod As Integer = x + n
Console.WriteLine(prod)
There are a number of other methods of doing this, for example using:
int.Parse(...)
or
Integer.TryParse(...)
More information on these sorts of type conversions can be found here:
http://dotnetperls.com/integer-parse-vbnet
One thing to bear in mind with these sorts of conversions is that you have to be certain that your input data is convertable. Otherwise your code will throw exceptions. This is where TryParse is useful as you can use this to check the inputs and handle invalid inputs without the need for exceptions.