What is taking IsNumeric() so long in .NET? - vb.net

Was doing some benchmarking with IsNumeric today and compared it to the following function:
Private Function IsNumeric(ByVal str As String) As Boolean
If String.IsNullOrEmpty(Str) Then Return False
Dim c As Char
For i As Integer = 0 To Str.Length - 1
c = Str(i)
If Not Char.IsNumber(c) Then Return False
Next
Return True
End Function
I was pretty surprised with the result.
With a numeric value this one was about 8-10 times faster then regular IsNumeric(), and with an empty or non-numeric value it was 1000-1500 times faster.
What is taking IsNumeric so long? Is there something else going on under the hood that I should consider before replacing it with the function above?
I use IsNumeric in about 50 different places all over my site, mostly for validation of forms and query strings.

Where is your check for locale-specific delimiters and decimal places? Negation? Exponential notation?
You see, your function is only a tiny subset of what numeric strings can be.
1,000,000.00
1,5E59
-123456789
You're missing all of these.

A single char can only contains 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
but a full string may contains any of the following:
1
1234
12.34
-1234
-12.34
0001
so IsNumeric ought to be somewhat slower.
There're also cultural and internationalization issues. i.e. If you are processing a Unicode string, does IsNumeric also process numbers from all other languages as well?

Generally speaking, I would avoid duplicating or replacing language features like this, especially in a language like VB, which is bound to be implementing many use cases for you. At the very least, I would name your method something distinct so that it is not confusing to other developers.
The speed difference is bound to be the fact your code is doing far less work than the VB language feature is.

I would use Integer.TryParse() or Double.TryParse() depending on the data type you are using, since both of these account for regional differences.

Related

VB.Net -" console.write(Format((87.20 \ 43.60))) " returning result of 1 not 2, why?

The below line of code is returning as a 1 instead of a 2, for reasons I can't comprehend.
console.write(Format((87.20 \ 43.60)))
Surely this should return the result of 2 but I've checked in another environment and it returns a can anyone tell me why?
I have tried putting the code into a second environment but the result was the same I don't understand why it is returning 1 instead of 2, can anyone enlighten me?
Thanks for the help but found the answer.
Decimals are converted to Long before Integer division and due to this are subject to banker's rounding, multiplying both numbers by a 100 before the operation resolves the issue.
Source of information:
Learn Microsoft - VB.Net - \ Operator - Remarks
Thanks,
While I am glad you resolved your own question, I did want to provide an alternative.
When you use the integer division operator, you do not have control as to how the rounding should occur. Whereas if you do a floating-point division operator you can keep the precision and then use one of the built-in Math class methods (documentation) to specify how the number should round.
Take a look at this example:
Dim result = 87.20 / 43.60
Dim roundUp = Math.Ceiling(result)
Dim roundDown = Math.Floor(result)
Dim bankersRounding = Math.Round(result)
Fiddle: https://dotnetfiddle.net/LZEMXV
Because in your example you are using Console.Write (which treats the data as a String) you do not need to cast the value to an Integer data type. However, if you needed the value as an Integer, any one of those variables outside result can be safely converted to an Integer using the Parse method (documentation).

CInt vs. Math.Round in Visual Basic .NET

What is the difference between:
Dim a As Integer = CInt(2.2)
and
Dim a As Integer = Math.Round(2.2)
?
CInt returns an integer but will round the .5 to nearest even number so:
2 = CInt(2.5)
4 = CInt(3.5)
Are both true, which might not be what you want.
Math.Round can be told to round away from zero. But returns a double, so we still need to cast it
3 = CInt(Math.Round(2.5, MidpointRounding.AwayFromZero))
There is bigger differences in CInt(), Int() and Round()... and others.
Round has parameters of rounding, so it is flexible and user friendly. But it do not change variable type. No "type conversion".
Meanwhile CInt() is a bit cryptic as it rounds too. And it is doing "Type conversion" to integer.
2 = Int(2.555), 3 = CInt(2.555)
2 = Int(2.5), 2 = CInt(2.5)
Some documentation states:
When the fractional part of expression is exactly .5, CInt always rounds it to the nearest even number. For example, .5 rounds to 0, and 1.5 rounds to 2.
But I do not like that "exact 0.5", in real word it is "0.5000001"
So, doing integer math (like calculating bitmaps address Hi and Lo bytes) do not use CInt(). Use old school INT(). Until you get to negative numbers... see the fix() function.
If there is no need to convert type, use floor().
I think all this chaos of number conversion is for some sort of compatibility with some ancient software.
The difference between those two functions is that they do totally different things:
CInt converts to an Integer type
Math.Round rounds the value to the nearest Integer
Math.Round in this instance will get you 2.0, as specified by the MSDN documentation. You are also using the function incorrectly, see the MSDN link above.
Both will raise an Exception if conversion fails, you can use Try..Catch for this.
Side note: You're new to VB.NET, but you might want to try switching to C#. I find that it is a hybrid of VB.NET & C++ and it will be far easier for you to work with than VB.NET.

Cleanest way to convert a `Double` or `Single` to `Integer`, without rounding

Converting a floating-point number to an integer using either CInt or CType will cause the value of that number to be rounded. The Int function and Math.Floor may be used to convert a floating-point number to a whole number, rounding toward negative infinity, but both functions return floating-point values which cannot be implicitly used as Integer values without a cast.
Is there a concise and idiomatic alternative to IntVar = CInt(Int(FloatingPointVar));? Pascal included Round and Trunc functions which returned Integer; is there some equivalent in either the VB.NET language or in the .NET framework?
A similar question, CInt does not round Double value consistently - how can I remove the fractional part? was asked in 2011, but it simply asked if there was a way to convert a floating-point number to an integer; the answers suggested a two-step process, but it didn't go into any depth about what does or does not exist in the framework. I would find it hard to believe that the Framework wouldn't have something analogous to the Pascal Trunc function, given that such a thing will frequently be needed when performing graphical operations using floating-point operands [such operations need to be rendered as discrete pixels, and should be rounded in such a way that round(x)-1 = round(x-1) for all x that fit within the range of +/- (2^31-1); even if such operations are rounded, they should use Floor(x+0.5), rather than round-to-nearest-even, so as to ensure the above property]
Incidentally, in C# a typecast from Double to Int using (type)expr notation uses round-to-zero semantics; the fact that this differs from the VB.NET behavior suggests that one or both languages is using its own conversion routines rather an explicit conversion operator included in the Framework. It would seem likely that the Framework should define a conversion operator? Does such an operator exist within the framework? What does it do? Is there a way to invoke it from C# and/or VB.NET?
After some searching, it seems that VB has no clean way of accomplishing that, short of writing an extension method.
The C# (int) cast translates directly into conv.i4 in IL. VB has no such operators, and no framework function seems to provide an alternative.
Usenet had an interesting discussion about this back in 2005 – of course a lot has changed since then but I think this still holds.
You can use the Math.Truncate method.
Calculates the integral part of a specified double-precision floating-point number.
For example:
Dim a As double = 1.6666666
Dim b As Integer = Math.Truncate(a) ' b = 1
I know this is an old case but I saw no one suggest the Math.Round() function.
Yes Math.Round takes a double and returns a double. However it returns a number that has been rounded to a whole number. It should easily and concisely convert to an integer using cInt. Would that suffice?
cInt(math.round(10000.54564)) ' = 10001
cInt(math.round(10000.49564)) ' = 10000
You may need extract the Int part of a float number:
float num = 12.234;
string toint = "" + num;
string auxil = toint.Split('.');
int newnum = Int.Parse(auxil[0]);

CStr() vs. Str() vs. .ToString()

I want to know what exactly are the differences between CStr(), Str() and .ToString()?
Label1.Text = CStr(Int(Rnd() * 10))
and
Label1.Text = Str(Int(Rnd() * 10))
and
Label1.Text = Int(Rnd() * 10).ToString
If I use this condition:
If Label1.Text = "7" Then
'Some code here
End If
Str() doesn't work here. What's the difference?
ToString will call the .ToString() function on a particular instance.
In practice, this means that it will throw an exception if the object in
question is Nothing. However, you can implement .ToString() in your own
classes to get a useful string representation of your object, whereas
CType/CStr only work with built-in classes and interfaces.
CStr and CType(expression, String) are exactly equivalent (I'm not
sure where the other poster got the idea that CStr is faster). But they
aren't really functions, they're compiler directives that will emit very
different code depending on the declaration of expression. In most
cases, these directives call a bunch of internal VB code that tries to
get a reasonable string out of expression.
DirectCast(expression, String) assumes that the expression in
question really is a String and just casts it. It's the fastest of all
these options, but will throw an exception if expression is anything
other than a String.
As an Addition to the VBA/VB6 Environment where we have no ToString():
Str() is not aware of international representation. The decimal separator always is a dot (.).
As already mentioned above it prefixes the resulting string with a blank in case of positive values.
There also exists Str$(). The difference to Str() is the return type:
Str() returns a variant of type string, Str$() returns a string.
And Str$() is slightly faster then Str().
CStr() in contrast is aware of international representation. The decimal separator depends on the Windows international settings.
No additional prefixing for positive values will be done.
So if you need to convert a value type to a string and have to ensure a dot as a decimal separator and no prefixing blank, then use this syntax:
Dim d As Double
d = 123.456
Dim s As String
s = Trim(Str$(d))
I don't know about ToString() and i don't know about VB.NET
But in VB6 (Visual Basic 6):
Both of Cstr() and Str() converts values to string. but Cstr() is better because:
Str(): After converting to string it adds 1 space before positive numbers. for example: Str(22) > " 22"
Cstr(): After converting to string it never adds the above extra space - For best result use it with Trim() - Trim(Cstr(Variable))
Although not a problem in the code in the question, it is important to mention that Str() only converts numerical expressions to string, gives an error in other cases, so don't use it for converting values of a cell.
My answer is str() is evil as it always prepends a space for the sign character so if you are comparing values it fails. Instead use CStr() instead which does not do this.
You may comes across business logic that tries to do this:
Eg:
Dim sVar as String = "1"
Dim i as Integer = 1
console.write( cstr(i) = sVar )
Which outputs:
False
I lost a couple hours on this one as the code was quite deep in old code and was very difficult to grok in production environment where logging based debugging was all that was available.

ISNUMERIC('07213E71') = True?

SQL is detecting that the following string ISNUMERIC:
'07213E71'
I believe this is because the 'E' is being classed as a mathmatical symbol.
However, I need to ensure that only values which are whole integers are returned as True.
How can I do this?
07213E71 is a floating number 7213 with 71 zeros
You can use this ISNUMERIC(myValue + '.0e0') to test for whole integers. Slightly cryptic but works.
Another test is the double negative myValue NOT LIKE '%[^0-9]%' which allows only digits 0 to 9.
ISNUMERIC has other issues in that these all return 1: +, -,
To nitpick: This is a whole integer. It is equivalent to 7213 * 10 ^ 71.
In the documentation it says
ISNUMERIC returns 1 when the input expression evaluates to a valid integer, floating point number, money or decimal type; otherwise it returns 0. A return value of 1 guarantees that expression can be converted to one of these numeric types.
Your number is also float (with exponential notation), therefore the only way to have ISINTEGER is to define it yourself on SQL. Read the following link.
http://classicasp.aspfaq.com/general/what-is-wrong-with-isnumeric.html
Extras:
http://www.sqlteam.com/forums/topic.asp?TOPIC_ID=59049
http://www.tek-tips.com/faqs.cfm?fid=6423
I have encountered the same problem. IsNumeric accepts "$, €, +, -, etc" as valid inputs and Convert function throws errors because of this.
Using "LIKE" SQL statement fixed my problem. I hope it'll help the others
SELECT UnitCode, UnitGUID, Convert(int, UnitCode) AS IntUnitCode
FROM [NG_Data].[NG].[T_GLB_Unit]
WHERE ISNULL(UnitType,'') <>'Department'
AND UnitCode NOT LIKE '%[^0-9]%'
ORDER BY IntUnitCode
PS: don't blame me for using "UnitCode" as nvarchar :) It is an old project :)
You have to ensure it out of the call to the database, whatever the language you work with, and then pass the value to the query. Probably the SQL is understanding that value as a string.