My VBA is treating the letter D as a number, what is going on? [duplicate] - excel

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

Is it possible to Add 0 in Double Var in VB Net

I'm trying to add 0 before each number I'm having but it seems it's not working because I'm using double. I'm using double so I can input numbers such as 1.5 (hours) and translate it to seconds, minutes, and hours (Output should be 0 seconds, 30 minutes, and 1 hour and 01:30:00). I'm having no problems with the first output but I can't seem to do the second output (The desired 01:30:00 only displays 1:30:0). What I did first is I tried to convert the double variable to int32 then to string but it seems to not work. Here's the code:
If SecondsRemainder >= 0 And SecondsRemainder < 10 Then
SecondsRemainder = Convert.ToInt32(SecondsRemainder)
SecondsRemainder.ToString.PadLeft(2, "0")
End If
This line of code:
SecondsRemainder.ToString.PadLeft(2, "0")
Doesn't seem to do anything, am I missing something out? Or is there any other way I can do? Looking forward to your answers!
Let's look at this line piece by piece, and see what it actually does:
SecondsRemainder.ToString.PadLeft(2, "0")
We start with the SecondsRemainder variable. This variable is still a Double, in spite of the earlier code using Convert.ToInt32(). Remember, at it's core VB.Net is a statically typed language! When you declare a variable with a specific type, the variable's type can never change.
We now call the .ToString() method for this variable. Note this really is a method, not a property. Good practice for .Net is to include the parentheses when calling methods, even though they aren't strictly required with VB. If I reviewed that code, I'd ask you to change it to show the parentheses. Remember, we're also getting the Double version of this method, rather than the Integer version. You're probably okay here, but the double version can do weird things for formatting you might not expect from an integer.
Finally, we take the string result from the previous method and call PadLeft(). This mostly does what you expect. However, there is no overload that takes a number and a string. Frankly, I'm surprised this even compiles, and it tells me you likely don't have Option Strict set correctly. No self-respecting programmer runs with Option Strict Off anymore. The correct way to call this function is like this:
.PadLeft(2, "0"c)
Where the c suffix gives you a character value rather than a string value.
And that's it. We're done. This function returns a result. It does not modify the calling variable. So we've done all this work, and discard the result without actually changing anything.
What I would do to fix your issue is declare a new string variable to receive the result. Then I would use this code to assign to it:
'Create a string variable to hold your string result
Dim RemainderString As String = ""
'Use double literals to compare with double variables!
If SecondsRemainder >= 0.0 And SecondsRemainder < 10.0 Then
'Use a format string directly from the initial double value to create your string result
' and don't forget to assign it to a variable
RemainerString = SecondsRemainder.ToString("00")
End If
You may also want to use Math.Round() first, as this code would still create "01" from a 1.9999 input.
Finally, I'm wondering how you're using this SecondsRemainder value. VB.Net has a whole set of methods for building date and time strings and values, and a variable name like SecondsRemainder sounds like you're doing something the hard way that could be much MUCH easier.
I'm wondering if the answer is that you are approaching the problem incorrectly. You seem to be computing some time value. If so use TimeSpan.
Dim ts As TimeSpan = TimeSpan.FromHours(1.51#)
' ts.TotalSeconds
' ts.Seconds
Dim s As String = ts.ToString("hh\:mm\:ss")

Why does drop() method in Scala allow negative value and does not throw error?

I am trying to get my hands dirty with Scala where is I am playing with scala.collection.immutable.StringOps on the terminal. In the String method drop(), I tried executing this code
"stackoverflow".drop(-12)
and the output I received was
stackoverflow
The -12 was a typo but this result is unexpected as it should truncate 12 characters from last or be an error or exception. Because when we pass an integer into the drop(), it eliminates the first characters from the string equivalent to the number of arguments. Why is this behavior kept with this method in Scala? Can this become useful in some scenarios? What is the reason for this behavior?
Scala has many other different behaviors that other languages don't support such as
true > false is true and true < false is false.
The documentation says "If n is negative, don't drop any elements". I'm guessing the reason is that in some cases it lets you skip doing some math. For instance, let's say you want to drop from the start of two strings so that they are equal length. You could do:
def makeSameLength(s1: String, s2: String): (String, String) = {
(s1.drop(s1.size-s2.size), s2.drop(s2.size-s1.size))
}
Cases like this where you have to drop some variable number of elements from a sequence come up a lot, and usually if the math works out to a negative number, that means you just don't want to drop anything at all. So this saves you from having to add in some conditional logic, or include a max(x, 0) or similar.
I do agree it's a bit surprising, but can see why it might be useful.

Displaying positive symbol for positive elements in MATLAB array?

I have an array in MATLAB, and I wanted to display the positive symbol, "+" in front of positive elements, and keep the negative symbol, "-" in already existing negative values. I thought I could do the following:
I was thinking of constructing a sort of cell string or string array, and having an if, else system where if the numbers magnitude was >0, then I should store the value as '+' concatenated with the conversion of the element. If it was 0, just do a straight up char conversion since 0 has no sign, and if it was negative, just convert it. I know what to do, however, logistically, I think my order of commands is whacky.
How can I implement this?
I have the following script for an array x, but it just spews out values, I want an orderly string array I can copy and paste for use outside of MATLAB.
x;
pos = '+';
bound = length(x);
for i=1:bound
if(x(i)==0)
num2str(x(i))
end
if(x(i)>0)
num2str(x(i))
strcat(pos,num2str(x(i)))
end
if(x(i)<0)
num2str(x(i))
strcat(pos,num2str(x(i)))
end
end
I think you are searching for this.
Let's make an example.
First type in your command window :
test = 5;
Then:
sprintf('%+d',test)
You should have in this way what you want.
Of course you need to adapt it to your case. I suggest you to read this.
I hope it helps.

Are VBA strings immutable?

I know .NET (and therefore VB.NET) strings are immutable. However, I'm using VBA 7.0 in Excel 2010. Are strings there immutable? I'm doing a lot of string processing and for small quantities, some (direct) string manipulation is fine, but I'm worried it won't scale - since every additional character moved from one string to another might create yet another instance of the string.
They are immutable, except when they exhibit mutable behaviour, then they are not.
For example assignment via mid$() is significantly faster than a normal new-string-from-assignment.
Dim s As String
s = "ABC"
Debug.Print s, StrPtr(s)
'// -> ABC 122899836
Mid$(s, 1, 1) = "Z"
Debug.Print s, StrPtr(s)
'// -> ZBC 122899836
s = "??" & Right$(s, 1)
Debug.Print s, StrPtr(s)
'// -> ??C 196635748
While VB.NET strings are immutable, as mandated by System.String, VBA (including VB6?) strings can be mutated (such as with Mid$). See Alex K's answer and note the StrPtr result after the operations.
Original answer; supported by documentation, in opposition to a counter-example.
VBA strings are immutable.
Just as with VB.NET, there is no way to "replace part of" or "append to" a string without creating a new string. Whether or not this matters - as modern computers are pretty darn fast - depends on the actual algorithm, data, and environment.
Unlike .NET documentation, such a behavior reference for VBA is [becoming] difficult to track down. From MS-VBAL: 2.1 Data Values and Value Types, we find this rare little gem
Individual data values are immutable. This means that there are no defined mechanisms available within a VBA Environment that can cause a data value to change into another data value.
where Strings represent "individual data values".
VBA strings ARE mutable. Alex K's answer is wrong but he is right about the Mid()= fudge in Microsoft's VB.Net which rightly claims they are immutable -- https://learn.microsoft.com/en-us/dotnet/visual-basic/programming-guide/language-features/strings/string-basics -- despite supporting Mid()=.
So, in all VB6, VBA, VB.Net, you can use (eg) Mid(a$, 1, 2)="hi" as stated in Alex's reply, to overwrite parts of a string. However, as user2864740 points out, only VB6 & VBA do this extremely quickly. This is tested code in VB6(VBA) & VB.Net..
Dim n1&, start!, bigstring$
bigstring$ = Space(10 ^ 6)
start = timer() ' in vb.Net this is function with "timer = (DateTime.Now.Ticks - Today.Ticks) / 10000000.0#"
For n1 = 1 To 5000
Mid(bigstring, n1, 1) = "1"
Next
Debug.Print("Elapsed millisecs: " & (timer() - start!) * 1000.0! & " over cycle count of " & n1 - 1)
On large strings over a megabyte, this technique is essential for speed and is thus scalable. On VBA, it does this in under a millisecond. However, in VB.Net it is stunningly inefficient -- this same test takes 7 seconds on a 3GHz CPU, about 1ms per iteration. With increasing from 5000 to 1 million iterations, VBA still only takes 40ms. VB.Net would take 30 mins. Tip: In VB.Net, you can use Dim BigByte() as byte then BigByte = System.Text.Encoding.UTF8.GetBytes(bigstring) so you are working with a byte array. How to make that flexible and Unicode is beyond this topic.

How and when are variant type are converted to regular data types

When the actual data type of a variable will be decided?
For ex:
x=10 here x will hold integer
x="Hello" here x will hold string
My basic question is msgbox "2"+"3" is 23 because these are strings and + is for concatenation so the result is 23
Then how the result of msgbox "2"*"3" becomes 6? where the string will be converted to integers and returns 6
If you are talking about using Visual Basic (you have not specified a language) then here is what I believe is happening:
The MsgBox function is expecting a and Object to turn into a String. (or at least it is trying to convert a String before it is displayed). Since "+" is a legit operator for concatenation, the first example can be directly converted to a String and returned.
In the second example, the asterisk is not a legit String operator, so it then has to attempt to convert your String segments into Integers. It does, then multiplies them, then the MsgBox converts the numerical expression back into a String and displays it.

Resources